From db4de675e9961cb0f918fedb19ad1c78a4a48bf6 Mon Sep 17 00:00:00 2001 From: Halil Ozercan Date: Wed, 7 Feb 2024 23:48:45 +0000 Subject: [PATCH 1/2] Introduce richtext-markdown as a generic Markdown renderer Markdown unfortunately does not have a universally agreed standard. Different flavors, libraries can parse Markdown content in their own ways. `richtext-commonmark` uses `Commonmark` spec and parser to convert any given markdown String into an intermediate Abstract Syntax Tree that has been defined internally since its release. The idea has always been to extract this ASTNode structure out of this module to enable custom parsing logic to be supplied from outside, and make `richtext-commonmark` only responsible for rendering the given tree using `richtext-ui`. This change introduces a new module named `richtext-markdown` which is actually just a rename of the existing `richtext-commonmark`. This new module also exposes its internal `AstNode` APIs to enable other modules to render their own AstNode structures. `richtext-commonmark` becomes a helper module which can parse any given string into an AstNode via `CommonmarkAstNodeParser`. Also, it still provides a `RichTextScope.Markdown` function that simply does the conversion asynchronously, then calls the `Markdown(AstNode)` flavor from `richtext-markdown`. --- .../richtext/sample/MarkdownSample.kt | 4 +- desktop-sample/build.gradle.kts | 1 - .../com/halilibo/richtext/desktop/Main.kt | 3 +- docs/index.md | 2 +- docs/richtext-commonmark.md | 40 +++++++-- docs/richtext-markdown.md | 54 +++++++++++++ mkdocs.yml | 1 + richtext-commonmark/build.gradle.kts | 9 +-- .../AstNodeConvert.kt | 48 +++++------ .../AstNodeConvertKtTest.kt | 1 + .../halilibo/richtext/commonmark/Markdown.kt | 56 +++++++++++++ .../MarkdownParseOptions.kt | 2 +- .../richtext/markdown/node/AstNode.kt | 12 --- .../richtext/markdown/node/AstNodeLinks.kt | 32 -------- .../markdown/node/AstStrikethrough.kt | 8 -- .../richtext/markdown/node/AstTable.kt | 27 ------- richtext-markdown/build.gradle.kts | 55 +++++++++++++ richtext-markdown/gradle.properties | 2 + .../src/androidMain/AndroidManifest.xml | 0 .../halilibo/richtext/markdown/HtmlBlock.kt | 0 .../halilibo/richtext/markdown/RemoteImage.kt | 0 .../halilibo/richtext/markdown/HtmlBlock.kt | 0 .../halilibo/richtext/markdown/Markdown.kt | 27 ++----- .../richtext/markdown/MarkdownRichText.kt | 0 .../halilibo/richtext/markdown/RemoteImage.kt | 0 .../halilibo/richtext/markdown/RenderTable.kt | 0 .../richtext/markdown/TraverseUtils.kt | 0 .../richtext/markdown/node/AstNode.kt | 28 +++++++ .../richtext/markdown/node/AstNodeLinks.kt | 37 +++++++++ .../richtext/markdown/node/AstNodeType.kt | 81 +++++++++++++------ .../richtext/markdown/node/AstTable.kt | 27 +++++++ .../halilibo/richtext/markdown/HtmlBlock.kt | 0 .../halilibo/richtext/markdown/RemoteImage.kt | 0 settings.gradle.kts | 1 + 34 files changed, 389 insertions(+), 169 deletions(-) create mode 100644 docs/richtext-markdown.md rename richtext-commonmark/src/commonJvmAndroid/kotlin/com/halilibo/richtext/{markdown => commonmark}/AstNodeConvert.kt (87%) rename richtext-commonmark/src/commonJvmAndroidTest/kotlin/com/halilibo/richtext/{markdown => commonmark}/AstNodeConvertKtTest.kt (93%) create mode 100644 richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/commonmark/Markdown.kt rename richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/{markdown => commonmark}/MarkdownParseOptions.kt (89%) delete mode 100644 richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNode.kt delete mode 100644 richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeLinks.kt delete mode 100644 richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstStrikethrough.kt delete mode 100644 richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstTable.kt create mode 100644 richtext-markdown/build.gradle.kts create mode 100644 richtext-markdown/gradle.properties rename {richtext-commonmark => richtext-markdown}/src/androidMain/AndroidManifest.xml (100%) rename {richtext-commonmark => richtext-markdown}/src/androidMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt (100%) rename {richtext-commonmark => richtext-markdown}/src/androidMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt (100%) rename {richtext-commonmark => richtext-markdown}/src/commonMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt (100%) rename {richtext-commonmark => richtext-markdown}/src/commonMain/kotlin/com/halilibo/richtext/markdown/Markdown.kt (88%) rename {richtext-commonmark => richtext-markdown}/src/commonMain/kotlin/com/halilibo/richtext/markdown/MarkdownRichText.kt (100%) rename {richtext-commonmark => richtext-markdown}/src/commonMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt (100%) rename {richtext-commonmark => richtext-markdown}/src/commonMain/kotlin/com/halilibo/richtext/markdown/RenderTable.kt (100%) rename {richtext-commonmark => richtext-markdown}/src/commonMain/kotlin/com/halilibo/richtext/markdown/TraverseUtils.kt (100%) create mode 100644 richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNode.kt create mode 100644 richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeLinks.kt rename {richtext-commonmark => richtext-markdown}/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeType.kt (55%) create mode 100644 richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstTable.kt rename {richtext-commonmark => richtext-markdown}/src/jvmMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt (100%) rename {richtext-commonmark => richtext-markdown}/src/jvmMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt (100%) diff --git a/android-sample/src/main/java/com/zachklipp/richtext/sample/MarkdownSample.kt b/android-sample/src/main/java/com/zachklipp/richtext/sample/MarkdownSample.kt index b185d380..37fabea1 100644 --- a/android-sample/src/main/java/com/zachklipp/richtext/sample/MarkdownSample.kt +++ b/android-sample/src/main/java/com/zachklipp/richtext/sample/MarkdownSample.kt @@ -30,8 +30,8 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em -import com.halilibo.richtext.markdown.Markdown -import com.halilibo.richtext.markdown.MarkdownParseOptions +import com.halilibo.richtext.commonmark.Markdown +import com.halilibo.richtext.commonmark.MarkdownParseOptions import com.halilibo.richtext.ui.RichTextStyle import com.halilibo.richtext.ui.material.RichText import com.halilibo.richtext.ui.resolveDefaults diff --git a/desktop-sample/build.gradle.kts b/desktop-sample/build.gradle.kts index 6b424ea9..4f5b0b6c 100644 --- a/desktop-sample/build.gradle.kts +++ b/desktop-sample/build.gradle.kts @@ -1,4 +1,3 @@ -import org.jetbrains.compose.compose import org.jetbrains.compose.desktop.application.dsl.TargetFormat plugins { diff --git a/desktop-sample/src/main/kotlin/com/halilibo/richtext/desktop/Main.kt b/desktop-sample/src/main/kotlin/com/halilibo/richtext/desktop/Main.kt index 58b649fb..1594bebe 100644 --- a/desktop-sample/src/main/kotlin/com/halilibo/richtext/desktop/Main.kt +++ b/desktop-sample/src/main/kotlin/com/halilibo/richtext/desktop/Main.kt @@ -27,12 +27,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp import androidx.compose.ui.window.singleWindowApplication -import com.halilibo.richtext.markdown.Markdown +import com.halilibo.richtext.commonmark.Markdown import com.halilibo.richtext.ui.CodeBlockStyle import com.halilibo.richtext.ui.RichTextStyle import com.halilibo.richtext.ui.material.RichText diff --git a/docs/index.md b/docs/index.md index ff479660..896282d4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -6,7 +6,7 @@ Compose Richtext is a collection of Compose libraries for working with rich text formatting and documents. -`richtext-ui`, `richtext-commonmark`, and `richtext-ui-material`|`richtext-ui-material3` are Kotlin Multiplatform(KMP) Compose Libraries. +`richtext-ui`, `richtext-markdown`, `richtext-commonmark`, and `richtext-ui-material`|`richtext-ui-material3` are Kotlin Multiplatform(KMP) Compose Libraries. All these modules can be used in Android and Desktop Compose apps. Each library is documented separately, see the navigation menu for the list. This site also includes diff --git a/docs/richtext-commonmark.md b/docs/richtext-commonmark.md index c8814dec..082f4557 100644 --- a/docs/richtext-commonmark.md +++ b/docs/richtext-commonmark.md @@ -3,7 +3,7 @@ [![Android Library](https://img.shields.io/badge/Platform-Android-green.svg?style=for-the-badge)](https://developer.android.com/studio/build/dependencies) [![JVM Library](https://img.shields.io/badge/Platform-JVM-red.svg?style=for-the-badge)](https://kotlinlang.org/docs/mpp-intro.html) -Library for rendering Markdown in Compose using [CommonMark](https://github.com/commonmark/commonmark-java) +Library for parsing and rendering Markdown in Compose using [CommonMark](https://github.com/commonmark/commonmark-java) library/spec to parse, and `richtext-ui` to render. ## Gradle @@ -14,7 +14,37 @@ dependencies { } ``` -## Usage +## Parsing + +`richtext-markdown` module renders a given Markdown Abstract Syntax Tree. It accepts a root +`AstNode`. This library gives you a parser called `CommonmarkAstNodeParser` to easily convert any +String to an `AstNode` that represents the Markdown tree. + +```kotlin + val parser = CommonmarkAstNodeParser() + val astNode = parser.parse( + """ + # Demo + + Emphasis, aka italics, with *asterisks* or _underscores_. Strong emphasis, aka bold, with **asterisks** or __underscores__. Combined emphasis with **asterisks and _underscores_**. [Links with two blocks, text in square-brackets, destination is in parentheses.](https://www.example.com). Inline `code` has `back-ticks around` it. + + 1. First ordered list item + 2. Another item + * Unordered sub-list. + 3. And another item. + You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown). + + * Unordered list can use asterisks + - Or minuses + + Or pluses + """.trimIndent() + ) + // ... + + RichTextScope.Markdown(astNode) +``` + +## Rendering The simplest way to render markdown is just pass a string to the [`Markdown`](../api/richtext-commonmark/com.halilibo.richtext.markdown/-markdown.html) composable under RichText scope: @@ -61,12 +91,12 @@ Which produces something like this: ![markdown demo](img/markdown-demo.png) -## MarkdownParseOptions +## [`MarkdownParseOptions`](../api/richtext-commonmark/com.halilibo.richtext.commonmark/-markdown-parse-options.html) -Passing `MarkdownParseOptions` into `Markdown` provides the ability to control some aspects of the markdown parser: +Passing `MarkdownParseOptions` into either `Markdown` composable or `CommonmarkAstNodeParser.parse` method provides the ability to control some aspects of the markdown parser: ```kotlin -val markdownParseOptions = MarkdownParseOptions.Default.copy( +val markdownParseOptions = MarkdownParseOptions( autolink = false ) diff --git a/docs/richtext-markdown.md b/docs/richtext-markdown.md new file mode 100644 index 00000000..e77eb84d --- /dev/null +++ b/docs/richtext-markdown.md @@ -0,0 +1,54 @@ +# Markdown + +[![Android Library](https://img.shields.io/badge/Platform-Android-green.svg?style=for-the-badge)](https://developer.android.com/studio/build/dependencies) +[![JVM Library](https://img.shields.io/badge/Platform-JVM-red.svg?style=for-the-badge)](https://kotlinlang.org/docs/mpp-intro.html) + +Library for rendering Markdown tree defined as an `AstNode`. + +## Gradle + +```kotlin +dependencies { + implementation("com.halilibo.compose-richtext:richtext-markdown:${richtext_version}") +} +``` + +## Usage + +`richtext-markdown` module renders a given Markdown Abstract Syntax Tree. It accepts a root +`AstNode`. However, this library does not include a parser to convert a regular Markdown String into +an `AstNode`. Please refer to `richtext-commonmark` for a sample implementation or a quick way to +render Markdown. + +## Rendering + +The simplest way to render markdown is just pass an `AstNode` to the [`Markdown`](../api/richtext-commonmark/com.halilibo.richtext.markdown/-markdown.html) +composable under RichText scope: + +~~~kotlin +RichText( + modifier = Modifier.padding(16.dp) +) { + val parser = remember(options) { CommonmarkAstNodeParser(options) } + val astNode = remember(parser) { + parser.parse( + """ + # Demo + + Emphasis, aka italics, with *asterisks* or _underscores_. Strong emphasis, aka bold, with **asterisks** or __underscores__. Combined emphasis with **asterisks and _underscores_**. [Links with two blocks, text in square-brackets, destination is in parentheses.](https://www.example.com). Inline `code` has `back-ticks around` it. + + 1. First ordered list item + 2. Another item + * Unordered sub-list. + 3. And another item. + You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown). + + * Unordered list can use asterisks + - Or minuses + + Or pluses + """.trimIndent() + ) + } + Markdown(astNode) +} +~~~ diff --git a/mkdocs.yml b/mkdocs.yml index db4d2808..9a33b31f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,6 +29,7 @@ nav: - richtext-ui-material.md - richtext-ui-material3.md - richtext-ui.md + - richtext-markdown.md - richtext-commonmark.md - printing.md - slideshow.md diff --git a/richtext-commonmark/build.gradle.kts b/richtext-commonmark/build.gradle.kts index 33d6a5a1..44cd6e9f 100644 --- a/richtext-commonmark/build.gradle.kts +++ b/richtext-commonmark/build.gradle.kts @@ -9,7 +9,7 @@ repositories { } android { - namespace = "com.halilibo.richtext.markdown" + namespace = "com.halilibo.richtext.commonmark" } kotlin { @@ -17,8 +17,8 @@ kotlin { val commonMain by getting { dependencies { implementation(compose.runtime) - implementation(compose.foundation) api(project(":richtext-ui")) + api(project(":richtext-markdown")) } } val commonTest by getting @@ -26,8 +26,6 @@ kotlin { val androidMain by getting { kotlin.srcDir("src/commonJvmAndroid/kotlin") dependencies { - implementation(Compose.coil) - implementation(Commonmark.core) implementation(Commonmark.tables) implementation(Commonmark.strikethrough) @@ -38,9 +36,6 @@ kotlin { val jvmMain by getting { kotlin.srcDir("src/commonJvmAndroid/kotlin") dependencies { - implementation(compose.desktop.currentOs) - implementation(Network.okHttp) - implementation(Commonmark.core) implementation(Commonmark.tables) implementation(Commonmark.strikethrough) diff --git a/richtext-commonmark/src/commonJvmAndroid/kotlin/com/halilibo/richtext/markdown/AstNodeConvert.kt b/richtext-commonmark/src/commonJvmAndroid/kotlin/com/halilibo/richtext/commonmark/AstNodeConvert.kt similarity index 87% rename from richtext-commonmark/src/commonJvmAndroid/kotlin/com/halilibo/richtext/markdown/AstNodeConvert.kt rename to richtext-commonmark/src/commonJvmAndroid/kotlin/com/halilibo/richtext/commonmark/AstNodeConvert.kt index a9f3fa0a..7877270b 100644 --- a/richtext-commonmark/src/commonJvmAndroid/kotlin/com/halilibo/richtext/markdown/AstNodeConvert.kt +++ b/richtext-commonmark/src/commonJvmAndroid/kotlin/com/halilibo/richtext/commonmark/AstNodeConvert.kt @@ -1,11 +1,6 @@ -package com.halilibo.richtext.markdown +package com.halilibo.richtext.commonmark -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.produceState -import androidx.compose.runtime.remember import com.halilibo.richtext.markdown.node.AstBlockQuote -import com.halilibo.richtext.markdown.node.AstBulletList import com.halilibo.richtext.markdown.node.AstCode import com.halilibo.richtext.markdown.node.AstDocument import com.halilibo.richtext.markdown.node.AstEmphasis @@ -35,6 +30,7 @@ import com.halilibo.richtext.markdown.node.AstTableRoot import com.halilibo.richtext.markdown.node.AstTableRow import com.halilibo.richtext.markdown.node.AstText import com.halilibo.richtext.markdown.node.AstThematicBreak +import com.halilibo.richtext.markdown.node.AstUnorderedList import org.commonmark.ext.autolink.AutolinkExtension import org.commonmark.ext.gfm.strikethrough.Strikethrough import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension @@ -90,7 +86,7 @@ internal fun convert( val newNodeType: AstNodeType? = when (node) { is BlockQuote -> AstBlockQuote - is BulletList -> AstBulletList(bulletMarker = node.bulletMarker) + is BulletList -> AstUnorderedList(bulletMarker = node.bulletMarker) is Code -> AstCode(literal = node.literal) is Document -> AstDocument is Emphasis -> AstEmphasis(delimiter = node.openingDelimiter) @@ -186,26 +182,30 @@ internal fun convert( return newNode } -internal fun Node.convert() = convert(this) +public actual class CommonmarkAstNodeParser actual constructor( + options: MarkdownParseOptions +) { -@Composable -internal actual fun parsedMarkdownAst(text: String, options: MarkdownParseOptions): AstNode? { - val parser = remember(options) { - Parser.builder() - .extensions( - listOfNotNull( - TablesExtension.create(), - StrikethroughExtension.create(), - if (options.autolink) AutolinkExtension.create() else null - ) + private val parser = Parser.builder() + .extensions( + listOfNotNull( + TablesExtension.create(), + StrikethroughExtension.create(), + if (options.autolink) AutolinkExtension.create() else null ) - .build() - } + ) + .build() - val astRootNode by produceState(null, text, parser) { - value = parser.parse(text).convert() - } + public actual fun parse(text: String): AstNode { + val commonmarkNode = parser.parse(text) + ?: throw IllegalArgumentException( + "Could not parse the given text content into a meaningful Markdown representation!" + ) - return astRootNode + return convert(commonmarkNode) + ?: throw IllegalArgumentException( + "Could not convert the generated Commonmark Node into an ASTNode!" + ) + } } diff --git a/richtext-commonmark/src/commonJvmAndroidTest/kotlin/com/halilibo/richtext/markdown/AstNodeConvertKtTest.kt b/richtext-commonmark/src/commonJvmAndroidTest/kotlin/com/halilibo/richtext/commonmark/AstNodeConvertKtTest.kt similarity index 93% rename from richtext-commonmark/src/commonJvmAndroidTest/kotlin/com/halilibo/richtext/markdown/AstNodeConvertKtTest.kt rename to richtext-commonmark/src/commonJvmAndroidTest/kotlin/com/halilibo/richtext/commonmark/AstNodeConvertKtTest.kt index 1917865f..9247b567 100644 --- a/richtext-commonmark/src/commonJvmAndroidTest/kotlin/com/halilibo/richtext/markdown/AstNodeConvertKtTest.kt +++ b/richtext-commonmark/src/commonJvmAndroidTest/kotlin/com/halilibo/richtext/commonmark/AstNodeConvertKtTest.kt @@ -1,5 +1,6 @@ package com.halilibo.richtext.markdown +import com.halilibo.richtext.commonmark.convert import com.halilibo.richtext.markdown.node.AstImage import com.halilibo.richtext.markdown.node.AstNode import com.halilibo.richtext.markdown.node.AstNodeLinks diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/commonmark/Markdown.kt b/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/commonmark/Markdown.kt new file mode 100644 index 00000000..ee7f5de6 --- /dev/null +++ b/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/commonmark/Markdown.kt @@ -0,0 +1,56 @@ +package com.halilibo.richtext.commonmark + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.produceState +import androidx.compose.runtime.remember +import com.halilibo.richtext.markdown.Markdown +import com.halilibo.richtext.commonmark.MarkdownParseOptions.Companion +import com.halilibo.richtext.markdown.node.AstNode +import com.halilibo.richtext.ui.RichTextScope + +/** + * A composable that renders Markdown content according to Commonmark specification using RichText. + * + * @param content Markdown text. No restriction on length. + * @param markdownParseOptions Options for the Markdown parser. + * @param onLinkClicked A function to invoke when a link is clicked from rendered content. + */ +@Composable +public fun RichTextScope.Markdown( + content: String, + markdownParseOptions: MarkdownParseOptions = Companion.Default, + onLinkClicked: ((String) -> Unit)? = null +) { + val commonmarkAstNodeParser = remember(markdownParseOptions) { + CommonmarkAstNodeParser(markdownParseOptions) + } + + val astRootNode by produceState( + initialValue = null, + key1 = commonmarkAstNodeParser, + key2 = content + ) { + value = commonmarkAstNodeParser.parse(content) + } + + astRootNode?.let { + Markdown(astNode = it, onLinkClicked = onLinkClicked) + } +} + +/** + * A helper class that can convert any text content into an ASTNode tree and return its root. + */ +public expect class CommonmarkAstNodeParser( + options: MarkdownParseOptions = MarkdownParseOptions.Default +) { + + /** + * Parse markdown content and return Abstract Syntax Tree(AST). + * + * @param text Markdown text to be parsed. + * @param options Options for the Commonmark Markdown parser. + */ + public fun parse(text: String): AstNode +} \ No newline at end of file diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/MarkdownParseOptions.kt b/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/commonmark/MarkdownParseOptions.kt similarity index 89% rename from richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/MarkdownParseOptions.kt rename to richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/commonmark/MarkdownParseOptions.kt index c09af285..30756823 100644 --- a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/MarkdownParseOptions.kt +++ b/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/commonmark/MarkdownParseOptions.kt @@ -1,4 +1,4 @@ -package com.halilibo.richtext.markdown +package com.halilibo.richtext.commonmark /** * Allows configuration of the Markdown parser diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNode.kt b/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNode.kt deleted file mode 100644 index c0a7a087..00000000 --- a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNode.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.halilibo.richtext.markdown.node - -/** - * Generic AstNode implementation that can define any node in Abstract Syntax Tree. - * - * @param type A sealed class which is categorized into block, container, and leaf nodes. - * @param links Pointers to parent, sibling, child nodes. - */ -internal data class AstNode( - val type: AstNodeType, - val links: AstNodeLinks -) diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeLinks.kt b/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeLinks.kt deleted file mode 100644 index 3c23665a..00000000 --- a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeLinks.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.halilibo.richtext.markdown.node - -import androidx.compose.runtime.Immutable - -/** - * All the pointers that can exist for a node in an AST. - */ -@Immutable -internal data class AstNodeLinks( - var parent: AstNode? = null, - var firstChild: AstNode? = null, - var lastChild: AstNode? = null, - var previous: AstNode? = null, - var next: AstNode? = null -) { - - /** - * Stop infinite loop and only check towards bottom-right direction - */ - override fun equals(other: Any?): Boolean { - if (other !is AstNodeLinks) return false - - return firstChild == other.firstChild && next == other.next - } - - /** - * Stop infinite loop and only calculate towards bottom-right direction - */ - override fun hashCode(): Int { - return (firstChild ?: 0).hashCode() * 11 + (next ?: 0).hashCode() * 7 - } -} \ No newline at end of file diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstStrikethrough.kt b/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstStrikethrough.kt deleted file mode 100644 index 1b9a2d3d..00000000 --- a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstStrikethrough.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.halilibo.richtext.markdown.node - -import androidx.compose.runtime.Immutable - -@Immutable -internal data class AstStrikethrough( - val delimiter: String -) : AstInlineNodeType() diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstTable.kt b/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstTable.kt deleted file mode 100644 index 90d08a7e..00000000 --- a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstTable.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.halilibo.richtext.markdown.node - -import androidx.compose.runtime.Immutable - -@Immutable -internal object AstTableRoot: AstContainerBlockNodeType() - -@Immutable -internal object AstTableBody: AstContainerBlockNodeType() - -@Immutable -internal object AstTableHeader: AstContainerBlockNodeType() - -@Immutable -internal object AstTableRow: AstContainerBlockNodeType() - -@Immutable -internal data class AstTableCell( - val header: Boolean, - val alignment: AstTableCellAlignment -) : AstContainerBlockNodeType() - -internal enum class AstTableCellAlignment { - LEFT, - CENTER, - RIGHT -} diff --git a/richtext-markdown/build.gradle.kts b/richtext-markdown/build.gradle.kts new file mode 100644 index 00000000..2a6d3412 --- /dev/null +++ b/richtext-markdown/build.gradle.kts @@ -0,0 +1,55 @@ +plugins { + id("richtext-kmp-library") + id("org.jetbrains.compose") version Compose.desktopVersion + id("org.jetbrains.dokka") +} + +repositories { + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") +} + +android { + namespace = "com.halilibo.richtext.markdown" +} + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + implementation(compose.runtime) + implementation(compose.foundation) + api(project(":richtext-ui")) + } + } + val commonTest by getting + + val androidMain by getting { + dependencies { + implementation(Compose.coil) + + implementation(Commonmark.core) + implementation(Commonmark.tables) + implementation(Commonmark.strikethrough) + implementation(Commonmark.autolink) + } + } + + val jvmMain by getting { + dependencies { + implementation(compose.desktop.currentOs) + implementation(Network.okHttp) + + implementation(Commonmark.core) + implementation(Commonmark.tables) + implementation(Commonmark.strikethrough) + implementation(Commonmark.autolink) + } + } + + val jvmTest by getting { + dependencies { + implementation(Kotlin.Test.jdk) + } + } + } +} diff --git a/richtext-markdown/gradle.properties b/richtext-markdown/gradle.properties new file mode 100644 index 00000000..572446d8 --- /dev/null +++ b/richtext-markdown/gradle.properties @@ -0,0 +1,2 @@ +POM_NAME=Compose Richtext Commonmark +POM_DESCRIPTION=A library for rendering markdown in Compose using the Commonmark library. \ No newline at end of file diff --git a/richtext-commonmark/src/androidMain/AndroidManifest.xml b/richtext-markdown/src/androidMain/AndroidManifest.xml similarity index 100% rename from richtext-commonmark/src/androidMain/AndroidManifest.xml rename to richtext-markdown/src/androidMain/AndroidManifest.xml diff --git a/richtext-commonmark/src/androidMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt b/richtext-markdown/src/androidMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt similarity index 100% rename from richtext-commonmark/src/androidMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt rename to richtext-markdown/src/androidMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt diff --git a/richtext-commonmark/src/androidMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt b/richtext-markdown/src/androidMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt similarity index 100% rename from richtext-commonmark/src/androidMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt rename to richtext-markdown/src/androidMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt similarity index 100% rename from richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt rename to richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/Markdown.kt b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/Markdown.kt similarity index 88% rename from richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/Markdown.kt rename to richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/Markdown.kt index 4386cca7..5f244951 100644 --- a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/Markdown.kt +++ b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/Markdown.kt @@ -10,7 +10,7 @@ import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import com.halilibo.richtext.markdown.node.AstBlockQuote -import com.halilibo.richtext.markdown.node.AstBulletList +import com.halilibo.richtext.markdown.node.AstUnorderedList import com.halilibo.richtext.markdown.node.AstDocument import com.halilibo.richtext.markdown.node.AstFencedCodeBlock import com.halilibo.richtext.markdown.node.AstHeading @@ -42,42 +42,27 @@ import com.halilibo.richtext.ui.string.Text import com.halilibo.richtext.ui.string.richTextString /** - * A composable that renders Markdown content using RichText. + * A composable that renders Markdown content pointed by [astNode] into this [RichTextScope]. * - * @param content Markdown text. No restriction on length. - * @param markdownParseOptions Options for the Markdown parser. + * @param astNode Root node of Markdown tree. This can be obtained via a parser. * @param onLinkClicked A function to invoke when a link is clicked from rendered content. */ @Composable public fun RichTextScope.Markdown( - content: String, - markdownParseOptions: MarkdownParseOptions = MarkdownParseOptions.Default, + astNode: AstNode, onLinkClicked: ((String) -> Unit)? = null ) { val onLinkClickedState = rememberUpdatedState(onLinkClicked) - // Can't use UriHandlerAmbient.current::openUri here, - // see https://issuetracker.google.com/issues/172366483 val realLinkClickedHandler = onLinkClickedState.value ?: LocalUriHandler.current.let { remember { { url -> it.openUri(url) } } } CompositionLocalProvider(LocalOnLinkClicked provides realLinkClickedHandler) { - val markdownAst = parsedMarkdownAst(text = content, options = markdownParseOptions) - RecursiveRenderMarkdownAst(astNode = markdownAst) + RecursiveRenderMarkdownAst(astNode = astNode) } } -/** - * Parse markdown content and return Abstract Syntax Tree(AST). - * Composable is efficient thanks to remember construct. - * - * @param text Markdown text to be parsed. - * @param options Options for the Markdown parser. - */ -@Composable -internal expect fun parsedMarkdownAst(text: String, options: MarkdownParseOptions): AstNode? - /** * When parsed, markdown content or any other rich text can be represented as a tree. * The default markdown parser that is used in this project `common-markdown` also @@ -115,7 +100,7 @@ internal fun RichTextScope.RecursiveRenderMarkdownAst(astNode: AstNode?) { visitChildren(astNode) } } - is AstBulletList -> { + is AstUnorderedList -> { FormattedList( listType = Unordered, items = astNode.filterChildrenType().toList() diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/MarkdownRichText.kt b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/MarkdownRichText.kt similarity index 100% rename from richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/MarkdownRichText.kt rename to richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/MarkdownRichText.kt diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt similarity index 100% rename from richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt rename to richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/RenderTable.kt b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/RenderTable.kt similarity index 100% rename from richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/RenderTable.kt rename to richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/RenderTable.kt diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/TraverseUtils.kt b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/TraverseUtils.kt similarity index 100% rename from richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/TraverseUtils.kt rename to richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/TraverseUtils.kt diff --git a/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNode.kt b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNode.kt new file mode 100644 index 00000000..f83d32a4 --- /dev/null +++ b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNode.kt @@ -0,0 +1,28 @@ +package com.halilibo.richtext.markdown.node + +/** + * Generic AstNode implementation that can define any node in Abstract Syntax Tree. + * + * @param type A sealed class which is categorized into block and inline nodes. + * @param links Pointers to parent, sibling, child nodes. + */ +public class AstNode( + public val type: AstNodeType, + public val links: AstNodeLinks +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is AstNode) return false + + if (type != other.type) return false + if (links != other.links) return false + + return true + } + + override fun hashCode(): Int { + var result = type.hashCode() + result = 31 * result + links.hashCode() + return result + } +} diff --git a/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeLinks.kt b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeLinks.kt new file mode 100644 index 00000000..be5bbcce --- /dev/null +++ b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeLinks.kt @@ -0,0 +1,37 @@ +package com.halilibo.richtext.markdown.node + +import androidx.compose.runtime.Immutable + +/** + * All the pointers that can exist for a node in an AST. + * + * Links are mutable to make it possible to instantiate a Node which can then reconfigure its + * children and siblings. Please do not modify the links after an ASTNode is created and the scope + * is finished. + */ +@Immutable +public class AstNodeLinks( + public var parent: AstNode? = null, + public var firstChild: AstNode? = null, + public var lastChild: AstNode? = null, + public var previous: AstNode? = null, + public var next: AstNode? = null +) { + + override fun equals(other: Any?): Boolean { + if (other !is AstNodeLinks) return false + + return parent === other.parent && + firstChild === other.firstChild && + lastChild === other.lastChild && + previous === other.previous && + next === other.next + } + + /** + * Stop infinite loop and only calculate towards bottom-right direction + */ + override fun hashCode(): Int { + return (firstChild ?: 0).hashCode() * 11 + (next ?: 0).hashCode() * 7 + } +} \ No newline at end of file diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeType.kt b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeType.kt similarity index 55% rename from richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeType.kt rename to richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeType.kt index 3d53e6d3..e1851279 100644 --- a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeType.kt +++ b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstNodeType.kt @@ -23,32 +23,50 @@ import com.halilibo.richtext.ui.string.RichTextString * nodes are about styling(bold, italic, strikethrough, code). The rest contains links, images, * html content, and of course raw text. */ -internal sealed class AstNodeType +public sealed class AstNodeType //region AstBlockNodeType -internal sealed class AstBlockNodeType: AstNodeType() +public sealed class AstBlockNodeType: AstNodeType() //region AstContainerBlockNodeType -internal sealed class AstContainerBlockNodeType: AstBlockNodeType() +/** + * Defines a subtype of Block Node that can contain other nodes. + */ +public sealed class AstContainerBlockNodeType: AstBlockNodeType() +/** + * Usually defines the root of a markdown document. + */ @Immutable -internal object AstDocument : AstContainerBlockNodeType() +public object AstDocument : AstContainerBlockNodeType() +/** + * A block quote container that will indent its contents relative to its own indentation. + */ @Immutable -internal object AstBlockQuote : AstContainerBlockNodeType() +public object AstBlockQuote : AstContainerBlockNodeType() +/** + * Ordered or Unordered list item. + */ @Immutable -internal object AstListItem : AstContainerBlockNodeType() +public object AstListItem : AstContainerBlockNodeType() +/** + * A list type that marks its items with bullets to signify a lack of order. + */ @Immutable -internal data class AstBulletList( +public data class AstUnorderedList( val bulletMarker: Char ) : AstContainerBlockNodeType() +/** + * A list type that uses numbers to mark its items. + */ @Immutable -internal data class AstOrderedList( +public data class AstOrderedList( val startNumber: Int, val delimiter: Char ) : AstContainerBlockNodeType() @@ -57,23 +75,26 @@ internal data class AstOrderedList( //region AstLeafBlockNodeType -internal sealed class AstLeafBlockNodeType: AstBlockNodeType() +/** + * Defines a subtype of Block Node that can only contain plain text and full-length annotations. + */ +public sealed class AstLeafBlockNodeType: AstBlockNodeType() @Immutable -internal object AstThematicBreak : AstLeafBlockNodeType() +public object AstThematicBreak : AstLeafBlockNodeType() @Immutable -internal data class AstHeading( +public data class AstHeading( val level: Int ) : AstLeafBlockNodeType() @Immutable -internal data class AstIndentedCodeBlock( +public data class AstIndentedCodeBlock( val literal: String ) : AstLeafBlockNodeType() @Immutable -internal data class AstFencedCodeBlock( +public data class AstFencedCodeBlock( val fenceChar: Char, val fenceLength: Int, val fenceIndent: Int, @@ -82,19 +103,19 @@ internal data class AstFencedCodeBlock( ) : AstLeafBlockNodeType() @Immutable -internal data class AstHtmlBlock( +public data class AstHtmlBlock( val literal: String ) : AstLeafBlockNodeType() @Immutable -internal data class AstLinkReferenceDefinition( +public data class AstLinkReferenceDefinition( val label: String, val destination: String, val title: String ) : AstLeafBlockNodeType() @Immutable -internal object AstParagraph : AstLeafBlockNodeType() +public object AstParagraph : AstLeafBlockNodeType() //endregion @@ -102,48 +123,56 @@ internal object AstParagraph : AstLeafBlockNodeType() //region AstInlineNodeType -internal sealed class AstInlineNodeType: AstNodeType() +/** + * Defines a node type that can only apply to inline content. + */ +public sealed class AstInlineNodeType: AstNodeType() @Immutable -internal data class AstCode( +public data class AstCode( val literal: String ) : AstInlineNodeType() @Immutable -internal data class AstEmphasis( +public data class AstEmphasis( private val delimiter: String ) : AstInlineNodeType() @Immutable -internal data class AstStrongEmphasis( +public data class AstStrongEmphasis( private val delimiter: String ) : AstInlineNodeType() @Immutable -internal data class AstLink( +public data class AstStrikethrough( + val delimiter: String +) : AstInlineNodeType() + +@Immutable +public data class AstLink( val destination: String, val title: String ) : AstInlineNodeType() @Immutable -internal data class AstImage( +public data class AstImage( val title: String, val destination: String ) : AstInlineNodeType() @Immutable -internal data class AstHtmlInline( +public data class AstHtmlInline( val literal: String ) : AstInlineNodeType() @Immutable -internal object AstHardLineBreak : AstInlineNodeType() +public object AstHardLineBreak : AstInlineNodeType() @Immutable -internal object AstSoftLineBreak : AstInlineNodeType() +public object AstSoftLineBreak : AstInlineNodeType() @Immutable -internal data class AstText( +public data class AstText( val literal: String ) : AstInlineNodeType() diff --git a/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstTable.kt b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstTable.kt new file mode 100644 index 00000000..f2addc4f --- /dev/null +++ b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/node/AstTable.kt @@ -0,0 +1,27 @@ +package com.halilibo.richtext.markdown.node + +import androidx.compose.runtime.Immutable + +@Immutable +public object AstTableRoot: AstContainerBlockNodeType() + +@Immutable +public object AstTableBody: AstContainerBlockNodeType() + +@Immutable +public object AstTableHeader: AstContainerBlockNodeType() + +@Immutable +public object AstTableRow: AstContainerBlockNodeType() + +@Immutable +public data class AstTableCell( + val header: Boolean, + val alignment: AstTableCellAlignment +) : AstContainerBlockNodeType() + +public enum class AstTableCellAlignment { + LEFT, + CENTER, + RIGHT +} diff --git a/richtext-commonmark/src/jvmMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt b/richtext-markdown/src/jvmMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt similarity index 100% rename from richtext-commonmark/src/jvmMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt rename to richtext-markdown/src/jvmMain/kotlin/com/halilibo/richtext/markdown/HtmlBlock.kt diff --git a/richtext-commonmark/src/jvmMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt b/richtext-markdown/src/jvmMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt similarity index 100% rename from richtext-commonmark/src/jvmMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt rename to richtext-markdown/src/jvmMain/kotlin/com/halilibo/richtext/markdown/RemoteImage.kt diff --git a/settings.gradle.kts b/settings.gradle.kts index 8f7f9a90..2f7eeb59 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,6 +12,7 @@ include(":richtext-ui") include(":richtext-ui-material") include(":richtext-ui-material3") include(":richtext-commonmark") +include(":richtext-markdown") include(":android-sample") include(":desktop-sample") include(":slideshow") From d9f031c695aad322720e906057d38db89666c598 Mon Sep 17 00:00:00 2001 From: Halil Ozercan Date: Thu, 8 Feb 2024 23:54:19 +0000 Subject: [PATCH 2/2] Version bump 1.5.12 This change includes bunch of misc tech debt cleaning. - Use Compose Multiplatform dependencies instead of Google's Compose artifacts - Removed Previews from richtext-ui. - Renamed CodeBlock platform files. - Kotlin updated to 1.9.22 - Compose compiler update to 1.5.8 - Changed MarkdownSample to use CommonmarkAstNodeParser and do synchronous Markdown rendering. --- android-sample/build.gradle.kts | 9 ++- .../richtext/sample/MarkdownSample.kt | 65 ++++++++------- build.gradle.kts | 2 +- buildSrc/src/main/kotlin/Dependencies.kt | 15 +--- docs/richtext-commonmark.md | 2 +- docs/richtext-markdown.md | 16 ++-- docs/richtext-ui-material3.md | 2 +- printing/build.gradle.kts | 7 +- .../halilibo/richtext/commonmark/Markdown.kt | 4 +- richtext-markdown/gradle.properties | 4 +- .../{Markdown.kt => BasicMarkdown.kt} | 5 +- richtext-ui/build.gradle.kts | 4 - .../ui/{CodeBlock.kt => CodeBlock.android.kt} | 1 - .../ui/util/ConditionalTapGestureDetector.kt | 4 +- .../ui/{CodeBlock.kt => CodeBlock.desktop.kt} | 1 - .../richtext/ui/previews/BlockQuotePreview.kt | 41 ---------- .../richtext/ui/previews/CodeBlockPreview.kt | 46 ----------- .../ui/previews/FormattedListPreview.kt | 79 ------------------- .../richtext/ui/previews/HeadingPreview.kt | 41 ---------- .../richtext/ui/previews/TablePreview.kt | 54 ------------- .../richtext/ui/previews/TextPreview.kt | 20 ----- slideshow/build.gradle.kts | 7 +- 22 files changed, 73 insertions(+), 356 deletions(-) rename richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/{Markdown.kt => BasicMarkdown.kt} (97%) rename richtext-ui/src/androidMain/kotlin/com/halilibo/richtext/ui/{CodeBlock.kt => CodeBlock.android.kt} (93%) rename richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/{CodeBlock.kt => CodeBlock.desktop.kt} (97%) delete mode 100644 richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/BlockQuotePreview.kt delete mode 100644 richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/CodeBlockPreview.kt delete mode 100644 richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/FormattedListPreview.kt delete mode 100644 richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/HeadingPreview.kt delete mode 100644 richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/TablePreview.kt delete mode 100644 richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/TextPreview.kt diff --git a/android-sample/build.gradle.kts b/android-sample/build.gradle.kts index 451f9463..3c749205 100644 --- a/android-sample/build.gradle.kts +++ b/android-sample/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id("com.android.application") kotlin("android") + id("org.jetbrains.compose") version Compose.desktopVersion } android { @@ -36,8 +37,8 @@ dependencies { implementation(project(":slideshow")) implementation(AndroidX.appcompat) implementation(Compose.activity) - implementation(Compose.foundation) - implementation(Compose.icons) - implementation(Compose.material) - implementation(Compose.tooling) + implementation(compose.foundation) + implementation(compose.materialIconsExtended) + implementation(compose.material) + implementation(compose.uiTooling) } diff --git a/android-sample/src/main/java/com/zachklipp/richtext/sample/MarkdownSample.kt b/android-sample/src/main/java/com/zachklipp/richtext/sample/MarkdownSample.kt index 37fabea1..427853fd 100644 --- a/android-sample/src/main/java/com/zachklipp/richtext/sample/MarkdownSample.kt +++ b/android-sample/src/main/java/com/zachklipp/richtext/sample/MarkdownSample.kt @@ -4,6 +4,8 @@ import android.widget.Toast import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -30,8 +32,9 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em -import com.halilibo.richtext.commonmark.Markdown +import com.halilibo.richtext.commonmark.CommonmarkAstNodeParser import com.halilibo.richtext.commonmark.MarkdownParseOptions +import com.halilibo.richtext.markdown.BasicMarkdown import com.halilibo.richtext.ui.RichTextStyle import com.halilibo.richtext.ui.material.RichText import com.halilibo.richtext.ui.resolveDefaults @@ -41,6 +44,7 @@ import com.halilibo.richtext.ui.resolveDefaults MarkdownSample() } +@OptIn(ExperimentalLayoutApi::class) @Composable fun MarkdownSample() { var richTextStyle by remember { mutableStateOf(RichTextStyle().resolveDefaults()) } var isDarkModeEnabled by remember { mutableStateOf(false) } @@ -70,29 +74,29 @@ import com.halilibo.richtext.ui.resolveDefaults // Config Card(elevation = 4.dp) { Column { - CheckboxPreference( - onClick = { - isDarkModeEnabled = !isDarkModeEnabled - }, - checked = isDarkModeEnabled, - label = "Dark Mode" - ) - - CheckboxPreference( - onClick = { - isWordWrapEnabled = !isWordWrapEnabled - }, - checked = isWordWrapEnabled, - label = "Word Wrap" - ) - - CheckboxPreference( - onClick = { - isAutolinkEnabled = !isAutolinkEnabled - }, - checked = isAutolinkEnabled, - label = "Autolink" - ) + FlowRow { + CheckboxPreference( + onClick = { + isDarkModeEnabled = !isDarkModeEnabled + }, + checked = isDarkModeEnabled, + label = "Dark Mode" + ) + CheckboxPreference( + onClick = { + isWordWrapEnabled = !isWordWrapEnabled + }, + checked = isWordWrapEnabled, + label = "Word Wrap" + ) + CheckboxPreference( + onClick = { + isAutolinkEnabled = !isAutolinkEnabled + }, + checked = isAutolinkEnabled, + label = "Autolink" + ) + } RichTextStyleConfig( richTextStyle = richTextStyle, @@ -104,13 +108,20 @@ import com.halilibo.richtext.ui.resolveDefaults SelectionContainer { Column(Modifier.verticalScroll(rememberScrollState())) { ProvideTextStyle(TextStyle(lineHeight = 1.3.em)) { + val parser = remember(markdownParseOptions) { + CommonmarkAstNodeParser(markdownParseOptions) + } + + val astNode = remember(parser) { + parser.parse(sampleMarkdown) + } + RichText( style = richTextStyle, modifier = Modifier.padding(8.dp), ) { - Markdown( - content = sampleMarkdown, - markdownParseOptions = markdownParseOptions, + BasicMarkdown( + astNode = astNode, onLinkClicked = { Toast.makeText(context, it, Toast.LENGTH_SHORT).show() } diff --git a/build.gradle.kts b/build.gradle.kts index c0055073..35ed7814 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -51,7 +51,7 @@ subprojects { allWarningsAsErrors = true } - freeCompilerArgs = listOf("-opt-in=kotlin.RequiresOptIn") + freeCompilerArgs = listOf("-opt-in=kotlin.RequiresOptIn", "-Xexpect-actual-classes") } } diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 452044e1..a6f9f852 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -16,7 +16,7 @@ object Network { object Kotlin { // keep in sync with buildSrc/build.gradle.kts - val version = "1.9.20" + val version = "1.9.22" val binaryCompatibilityValidatorPlugin = "org.jetbrains.kotlinx:binary-compatibility-validator:0.9.0" val gradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$version" @@ -31,19 +31,12 @@ val ktlint = "org.jlleitschuh.gradle:ktlint-gradle:10.0.0" object Compose { val version = "1.5.4" - val compilerVersion = "1.5.4" - val desktopVersion = "1.5.11" + val compilerVersion = "1.5.8" + val desktopVersion = "1.5.12" val activity = "androidx.activity:activity-compose:1.7.2" - val foundation = "androidx.compose.foundation:foundation:$version" - val material = "androidx.compose.material:material:$version" - val material3 = "androidx.compose.material3:material3:1.0.1" - val icons = "androidx.compose.material:material-icons-extended:$version" - val test = "androidx.ui:ui-test:$version" - val tooling = "androidx.compose.ui:ui-tooling:$version" val toolingData = "androidx.compose.ui:ui-tooling-data:$version" - val desktopPreview = "org.jetbrains.compose.ui:ui-tooling-preview-desktop:$desktopVersion" val multiplatformUiUtil = "org.jetbrains.compose.ui:ui-util:$desktopVersion" - val coil = "io.coil-kt:coil-compose:2.4.0" + val coil = "io.coil-kt:coil-compose:2.5.0" } object Commonmark { diff --git a/docs/richtext-commonmark.md b/docs/richtext-commonmark.md index 082f4557..ce0f7ad2 100644 --- a/docs/richtext-commonmark.md +++ b/docs/richtext-commonmark.md @@ -41,7 +41,7 @@ String to an `AstNode` that represents the Markdown tree. ) // ... - RichTextScope.Markdown(astNode) + RichTextScope.BasicMarkdown(astNode) ``` ## Rendering diff --git a/docs/richtext-markdown.md b/docs/richtext-markdown.md index e77eb84d..085e61a2 100644 --- a/docs/richtext-markdown.md +++ b/docs/richtext-markdown.md @@ -3,7 +3,11 @@ [![Android Library](https://img.shields.io/badge/Platform-Android-green.svg?style=for-the-badge)](https://developer.android.com/studio/build/dependencies) [![JVM Library](https://img.shields.io/badge/Platform-JVM-red.svg?style=for-the-badge)](https://kotlinlang.org/docs/mpp-intro.html) -Library for rendering Markdown tree defined as an `AstNode`. +Library for rendering Markdown tree that is defined as an `AstNode`. This module would be useless +for someone who is looking to just render a Markdown string. Please take a look to +`richtext-commonmark` for such features. `richtext-markdown` behaves as sort of a building block. +You can create your own parser or use 3rd party ones that converts any Markdown string to an +`AstNode` tree. ## Gradle @@ -13,13 +17,6 @@ dependencies { } ``` -## Usage - -`richtext-markdown` module renders a given Markdown Abstract Syntax Tree. It accepts a root -`AstNode`. However, this library does not include a parser to convert a regular Markdown String into -an `AstNode`. Please refer to `richtext-commonmark` for a sample implementation or a quick way to -render Markdown. - ## Rendering The simplest way to render markdown is just pass an `AstNode` to the [`Markdown`](../api/richtext-commonmark/com.halilibo.richtext.markdown/-markdown.html) @@ -29,6 +26,7 @@ composable under RichText scope: RichText( modifier = Modifier.padding(16.dp) ) { + // requires richtext-commonmark module. val parser = remember(options) { CommonmarkAstNodeParser(options) } val astNode = remember(parser) { parser.parse( @@ -49,6 +47,6 @@ RichText( """.trimIndent() ) } - Markdown(astNode) + BasicMarkdown(astNode) } ~~~ diff --git a/docs/richtext-ui-material3.md b/docs/richtext-ui-material3.md index a9d9b8d7..a712090e 100644 --- a/docs/richtext-ui-material3.md +++ b/docs/richtext-ui-material3.md @@ -1,4 +1,4 @@ -# Richtext UI Material +# Richtext UI Material 3 [![Android Library](https://img.shields.io/badge/Platform-Android-green.svg?style=for-the-badge)](https://developer.android.com/studio/build/dependencies) [![JVM Library](https://img.shields.io/badge/Platform-JVM-red.svg?style=for-the-badge)](https://kotlinlang.org/docs/mpp-intro.html) diff --git a/printing/build.gradle.kts b/printing/build.gradle.kts index 21fb919b..5dba13d9 100644 --- a/printing/build.gradle.kts +++ b/printing/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id("richtext-android-library") id("org.jetbrains.dokka") + id("org.jetbrains.compose") version Compose.desktopVersion } android { @@ -8,14 +9,14 @@ android { } dependencies { - implementation(Compose.foundation) - implementation(Compose.tooling) + implementation(compose.foundation) + implementation(compose.uiTooling) // For slot table analysis. implementation(Compose.toolingData) implementation(Compose.activity) // TODO Migrate off this. - implementation(Compose.material) + implementation(compose.material) } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class.java).all { diff --git a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/commonmark/Markdown.kt b/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/commonmark/Markdown.kt index ee7f5de6..a3786156 100644 --- a/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/commonmark/Markdown.kt +++ b/richtext-commonmark/src/commonMain/kotlin/com/halilibo/richtext/commonmark/Markdown.kt @@ -4,7 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import androidx.compose.runtime.remember -import com.halilibo.richtext.markdown.Markdown +import com.halilibo.richtext.markdown.BasicMarkdown import com.halilibo.richtext.commonmark.MarkdownParseOptions.Companion import com.halilibo.richtext.markdown.node.AstNode import com.halilibo.richtext.ui.RichTextScope @@ -35,7 +35,7 @@ public fun RichTextScope.Markdown( } astRootNode?.let { - Markdown(astNode = it, onLinkClicked = onLinkClicked) + BasicMarkdown(astNode = it, onLinkClicked = onLinkClicked) } } diff --git a/richtext-markdown/gradle.properties b/richtext-markdown/gradle.properties index 572446d8..08c5bdd6 100644 --- a/richtext-markdown/gradle.properties +++ b/richtext-markdown/gradle.properties @@ -1,2 +1,2 @@ -POM_NAME=Compose Richtext Commonmark -POM_DESCRIPTION=A library for rendering markdown in Compose using the Commonmark library. \ No newline at end of file +POM_NAME=Compose Richtext Markdown +POM_DESCRIPTION=A library for rendering markdown represented as an AST in Compose. \ No newline at end of file diff --git a/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/Markdown.kt b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/BasicMarkdown.kt similarity index 97% rename from richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/Markdown.kt rename to richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/BasicMarkdown.kt index 5f244951..33f0100f 100644 --- a/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/Markdown.kt +++ b/richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/BasicMarkdown.kt @@ -43,12 +43,13 @@ import com.halilibo.richtext.ui.string.richTextString /** * A composable that renders Markdown content pointed by [astNode] into this [RichTextScope]. + * Designed to be a building block that should be wrapped with a specific parser. * * @param astNode Root node of Markdown tree. This can be obtained via a parser. * @param onLinkClicked A function to invoke when a link is clicked from rendered content. */ @Composable -public fun RichTextScope.Markdown( +public fun RichTextScope.BasicMarkdown( astNode: AstNode, onLinkClicked: ((String) -> Unit)? = null ) { @@ -186,7 +187,7 @@ internal fun RichTextScope.visitChildren(node: AstNode?) { } /** - * An internal ambient to pass through OnLinkClicked function from root [Markdown] composable + * An internal ambient to pass through OnLinkClicked function from root [BasicMarkdown] composable * to children that render links. Although being explicit is preferred, recursive calls to * [visitChildren] increases verbosity with each extra argument. */ diff --git a/richtext-ui/build.gradle.kts b/richtext-ui/build.gradle.kts index 5eb08c36..3c960c21 100644 --- a/richtext-ui/build.gradle.kts +++ b/richtext-ui/build.gradle.kts @@ -28,10 +28,6 @@ kotlin { } val jvmMain by getting { kotlin.srcDir("src/commonJvmAndroid/kotlin") - dependencies { - // requires installing https://plugins.jetbrains.com/plugin/16541-compose-multiplatform-ide-support - implementation(Compose.desktopPreview) - } } } } diff --git a/richtext-ui/src/androidMain/kotlin/com/halilibo/richtext/ui/CodeBlock.kt b/richtext-ui/src/androidMain/kotlin/com/halilibo/richtext/ui/CodeBlock.android.kt similarity index 93% rename from richtext-ui/src/androidMain/kotlin/com/halilibo/richtext/ui/CodeBlock.kt rename to richtext-ui/src/androidMain/kotlin/com/halilibo/richtext/ui/CodeBlock.android.kt index c045dad1..3368bda7 100644 --- a/richtext-ui/src/androidMain/kotlin/com/halilibo/richtext/ui/CodeBlock.kt +++ b/richtext-ui/src/androidMain/kotlin/com/halilibo/richtext/ui/CodeBlock.android.kt @@ -1,4 +1,3 @@ -@file:JvmName("CodeBlockAndroid") package com.halilibo.richtext.ui import androidx.compose.foundation.horizontalScroll diff --git a/richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/util/ConditionalTapGestureDetector.kt b/richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/util/ConditionalTapGestureDetector.kt index 7e0f7c21..9796d476 100644 --- a/richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/util/ConditionalTapGestureDetector.kt +++ b/richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/util/ConditionalTapGestureDetector.kt @@ -6,13 +6,11 @@ import androidx.compose.foundation.gestures.GestureCancellationException import androidx.compose.foundation.gestures.PressGestureScope import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.gestures.forEachGesture import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.AwaitPointerEventScope -import androidx.compose.ui.input.pointer.PointerEventTimeoutCancellationException import androidx.compose.ui.input.pointer.PointerEventPass +import androidx.compose.ui.input.pointer.PointerEventTimeoutCancellationException import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputScope import androidx.compose.ui.input.pointer.changedToUp diff --git a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/CodeBlock.kt b/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/CodeBlock.desktop.kt similarity index 97% rename from richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/CodeBlock.kt rename to richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/CodeBlock.desktop.kt index e56b52a6..8b101dd8 100644 --- a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/CodeBlock.kt +++ b/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/CodeBlock.desktop.kt @@ -1,4 +1,3 @@ -@file:JvmName("CodeBlockJvm") package com.halilibo.richtext.ui import androidx.compose.foundation.HorizontalScrollbar diff --git a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/BlockQuotePreview.kt b/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/BlockQuotePreview.kt deleted file mode 100644 index aeab4929..00000000 --- a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/BlockQuotePreview.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.halilibo.richtext.ui.previews - -import androidx.compose.desktop.ui.tooling.preview.Preview -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import com.halilibo.richtext.ui.BlockQuote -import com.halilibo.richtext.ui.LocalInternalContentColor -import com.halilibo.richtext.ui.RichTextScope -import com.halilibo.richtext.ui.Text - -@Preview -@Composable -private fun BlockQuotePreviewOnWhite() { - BlockQuotePreview(backgroundColor = Color.White, contentColor = Color.Black) -} - -@Preview -@Composable private fun BlockQuotePreviewOnBlack() { - BlockQuotePreview(backgroundColor = Color.Black, contentColor = Color.White) -} - -@Composable private fun BlockQuotePreview( - backgroundColor: Color, - contentColor: Color -) { - CompositionLocalProvider(LocalInternalContentColor provides contentColor) { - Box(Modifier.background(backgroundColor)) { - RichTextScope.BlockQuote { - Text("Some text.") - Text("Another paragraph.") - BlockQuote { - Text("Nested block quote.") - } - } - } - } -} diff --git a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/CodeBlockPreview.kt b/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/CodeBlockPreview.kt deleted file mode 100644 index 1f8df1bc..00000000 --- a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/CodeBlockPreview.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.halilibo.richtext.ui.previews - -import androidx.compose.desktop.ui.tooling.preview.Preview -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import com.halilibo.richtext.ui.CodeBlock -import com.halilibo.richtext.ui.LocalInternalContentColor -import com.halilibo.richtext.ui.RichTextScope - -@Preview -@Composable -private fun CodeBlockPreviewOnWhite() { - CodeBlockPreview(backgroundColor = Color.White, contentColor = Color.Black) -} - -@Preview -@Composable -private fun CodeBlockPreviewOnBlack() { - CodeBlockPreview(backgroundColor = Color.Black, contentColor = Color.White) -} - -@Composable -private fun CodeBlockPreview( - backgroundColor: Color, - contentColor: Color -) { - CompositionLocalProvider(LocalInternalContentColor provides contentColor) { - Box(modifier = Modifier.background(color = backgroundColor)) { - Box(modifier = Modifier.padding(24.dp)) { - RichTextScope.CodeBlock( - """ - data class Hello( - val name: String - ) - """.trimIndent() - ) - } - } - } -} diff --git a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/FormattedListPreview.kt b/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/FormattedListPreview.kt deleted file mode 100644 index 98efc828..00000000 --- a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/FormattedListPreview.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.halilibo.richtext.ui.previews - -import androidx.compose.desktop.ui.tooling.preview.Preview -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.unit.LayoutDirection -import com.halilibo.richtext.ui.FormattedList -import com.halilibo.richtext.ui.ListType -import com.halilibo.richtext.ui.RichTextScope -import com.halilibo.richtext.ui.Text - -@Preview -@Composable -private fun UnorderedListPreview() { - ListPreview(listType = ListType.Unordered, layoutDirection = LayoutDirection.Ltr) -} - -@Preview -@Composable -private fun UnorderedListPreviewRtl() { - ListPreview(listType = ListType.Unordered, layoutDirection = LayoutDirection.Rtl) -} - -@Preview -@Composable -private fun OrderedListPreview() { - ListPreview(listType = ListType.Ordered, layoutDirection = LayoutDirection.Ltr) -} - -@Preview -@Composable -private fun OrderedListPreviewRtl() { - ListPreview(listType = ListType.Ordered, layoutDirection = LayoutDirection.Rtl) -} - -@Composable -private fun ListPreview( - listType: ListType, - layoutDirection: LayoutDirection -) { - CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) { - Box(Modifier.background(color = Color.White)) { - RichTextScope.FormattedList( - listType = listType, - items = listOf( - "Foo", - "Bar", - "Baz", - "Foo", - "Bar", - "Baz", - "Foo", - "Bar", - "Foo\nBar\nBaz", - "Foo" - ).withIndex() - .toList() - ) { (index, text) -> - Text(text) - if (index == 0) { - FormattedList(listType, @Composable { - Text("indented $text") - FormattedList(listType, @Composable { - Text("indented $text") - FormattedList(listType, @Composable { - Text("indented $text") - }) - }) - }) - } - } - } - } -} diff --git a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/HeadingPreview.kt b/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/HeadingPreview.kt deleted file mode 100644 index 56b26f5f..00000000 --- a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/HeadingPreview.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.halilibo.richtext.ui.previews - -import androidx.compose.desktop.ui.tooling.preview.Preview -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import com.halilibo.richtext.ui.Heading -import com.halilibo.richtext.ui.LocalInternalContentColor -import com.halilibo.richtext.ui.RichTextScope - -@Preview -@Composable -private fun HeadingPreviewOnWhite() { - HeadingPreview(backgroundColor = Color.White, contentColor = Color.Black) -} - -@Preview -@Composable -private fun HeadingPreviewOnBlack() { - HeadingPreview(backgroundColor = Color.Black, contentColor = Color.White) -} - -@Composable -private fun HeadingPreview( - backgroundColor: Color, - contentColor: Color -) { - CompositionLocalProvider(LocalInternalContentColor provides contentColor) { - Box(Modifier.background(color = backgroundColor)) { - Column { - for (level in 0 until 10) { - RichTextScope.Heading(level, "Heading ${level + 1}") - } - } - } - } -} diff --git a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/TablePreview.kt b/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/TablePreview.kt deleted file mode 100644 index 1ac580e9..00000000 --- a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/TablePreview.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.halilibo.richtext.ui.previews - -import androidx.compose.desktop.ui.tooling.preview.Preview -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import com.halilibo.richtext.ui.BlockQuote -import com.halilibo.richtext.ui.CodeBlock -import com.halilibo.richtext.ui.RichTextScope -import com.halilibo.richtext.ui.Table -import com.halilibo.richtext.ui.Text - -@Preview -@Composable -private fun TablePreview() { - TablePreviewContents() -} - -@Preview -@Composable -private fun TablePreviewFixedWidth() { - TablePreviewContents() -} - -@Composable -private fun TablePreviewContents(modifier: Modifier = Modifier) { - RichTextScope.Table( - modifier = modifier - .background(Color.White) - .padding(4.dp), - headerRow = { - cell { Text("Column 1") } - cell { Text("Column 2") } - } - ) { - row { - cell { Text("Hello") } - cell { - CodeBlock("Foo bar") - } - } - row { - cell { - BlockQuote { - Text("Stuff") - } - } - cell { Text("Hello world this is a really long line that is going to wrap hopefully") } - } - } -} diff --git a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/TextPreview.kt b/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/TextPreview.kt deleted file mode 100644 index 7501d4c3..00000000 --- a/richtext-ui/src/jvmMain/kotlin/com/halilibo/richtext/ui/previews/TextPreview.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.halilibo.richtext.ui.previews - -import androidx.compose.desktop.ui.tooling.preview.Preview -import androidx.compose.runtime.Composable -import com.halilibo.richtext.ui.RichTextScope -import com.halilibo.richtext.ui.string.RichTextString -import com.halilibo.richtext.ui.string.Text -import com.halilibo.richtext.ui.string.richTextString -import com.halilibo.richtext.ui.string.withFormat - -@Preview -@Composable -private fun TextPreview() { - RichTextScope.Text(richTextString { - append("I'm ") - withFormat(RichTextString.Format.Bold) { - append("bold!") - } - }) -} diff --git a/slideshow/build.gradle.kts b/slideshow/build.gradle.kts index c93de4e8..373ca7df 100644 --- a/slideshow/build.gradle.kts +++ b/slideshow/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id("richtext-android-library") id("org.jetbrains.dokka") + id("org.jetbrains.compose") version Compose.desktopVersion } android { @@ -8,7 +9,7 @@ android { } dependencies { - implementation(Compose.foundation) - implementation(Compose.material) - implementation(Compose.tooling) + implementation(compose.foundation) + implementation(compose.material) + implementation(compose.uiTooling) }