diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 5fc4870c..b6eaec83 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -11,10 +11,10 @@ jobs: steps: - uses: actions/checkout@v2 - uses: gradle/wrapper-validation-action@v1 - - name: set up JDK 17 + - name: set up JDK 21 uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 21 - uses: actions/cache@v4 with: path: ~/.gradle/caches diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c8ae0e70..8fb09994 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 21 - uses: actions/setup-python@v2 with: python-version: 3.x diff --git a/android-sample/build.gradle.kts b/android-sample/build.gradle.kts index 0f5729bb..75bea54f 100644 --- a/android-sample/build.gradle.kts +++ b/android-sample/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id("com.android.application") kotlin("android") @@ -22,8 +24,11 @@ android { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } - kotlinOptions { - jvmTarget = "11" +} + +kotlin { + compilerOptions { + jvmTarget = JvmTarget.JVM_11 } } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index aaf2e76d..a47ed4c1 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -10,8 +10,8 @@ plugins { dependencies { // keep in sync with Dependencies.BuildPlugins.androidGradlePlugin - implementation("com.android.tools.build:gradle:8.7.0") + implementation("com.android.tools.build:gradle:8.13.0") // keep in sync with Dependencies.Kotlin.gradlePlugin - implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21") + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.20") implementation(kotlin("script-runtime")) } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 9ff3451e..e260117e 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -1,10 +1,10 @@ object BuildPlugins { // keep in sync with buildSrc/build.gradle.kts - val androidGradlePlugin = "com.android.tools.build:gradle:8.7.0" + val androidGradlePlugin = "com.android.tools.build:gradle:8.13.0" } object AndroidX { - val appcompat = "androidx.appcompat:appcompat:1.3.0" + val appcompat = "androidx.appcompat:appcompat:1.7.1" } object Network { @@ -13,7 +13,7 @@ object Network { object Kotlin { // keep in sync with buildSrc/build.gradle.kts - val version = "2.0.21" + val version = "2.2.20" val binaryCompatibilityValidatorPlugin = "org.jetbrains.kotlinx:binary-compatibility-validator:0.9.0" val gradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$version" @@ -30,11 +30,12 @@ object Compose { val desktopVersion = "1.8.2" val activity = "androidx.activity:activity-compose:1.8.2" val toolingData = "androidx.compose.ui:ui-tooling-data:1.6.0" - val coil = "io.coil-kt:coil-compose:2.5.0" + val coil = "io.coil-kt.coil3:coil-compose:3.3.0" + val coilHttp = "io.coil-kt.coil3:coil-network-okhttp:3.3.0" } object Commonmark { - private val version = "0.25.0" + private val version = "0.26.0" val core = "org.commonmark:commonmark:$version" val tables = "org.commonmark:commonmark-ext-gfm-tables:$version" val strikethrough = "org.commonmark:commonmark-ext-gfm-strikethrough:$version" @@ -43,6 +44,6 @@ object Commonmark { object AndroidConfiguration { val minSdk = 23 - val targetSdk = 35 + val targetSdk = 36 val compileSdk = targetSdk } diff --git a/buildSrc/src/main/kotlin/richtext-android-library.gradle.kts b/buildSrc/src/main/kotlin/richtext-android-library.gradle.kts index 0d72f74b..2517ac98 100644 --- a/buildSrc/src/main/kotlin/richtext-android-library.gradle.kts +++ b/buildSrc/src/main/kotlin/richtext-android-library.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id("com.android.library") kotlin("android") @@ -5,6 +7,9 @@ plugins { kotlin { explicitApi() + compilerOptions { + jvmTarget = JvmTarget.JVM_11 + } } android { @@ -19,9 +24,6 @@ android { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } - kotlinOptions { - jvmTarget = "11" - } buildFeatures { compose = true diff --git a/buildSrc/src/main/kotlin/richtext-kmp-library.gradle.kts b/buildSrc/src/main/kotlin/richtext-kmp-library.gradle.kts index 5dc38896..f2cdcbec 100644 --- a/buildSrc/src/main/kotlin/richtext-kmp-library.gradle.kts +++ b/buildSrc/src/main/kotlin/richtext-kmp-library.gradle.kts @@ -1,3 +1,8 @@ +import AndroidConfiguration.compileSdk +import AndroidConfiguration.minSdk +import AndroidConfiguration.targetSdk +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id("com.android.library") kotlin("multiplatform") @@ -14,8 +19,8 @@ kotlin { jvm() androidTarget { publishLibraryVariants("release") - compilations.all { - kotlinOptions.jvmTarget = "11" + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) } } explicitApi() diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 09523c0e..37f853b1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/richtext-markdown/build.gradle.kts b/richtext-markdown/build.gradle.kts index 264c41d6..3fef99db 100644 --- a/richtext-markdown/build.gradle.kts +++ b/richtext-markdown/build.gradle.kts @@ -27,6 +27,7 @@ kotlin { val androidMain by getting { dependencies { implementation(Compose.coil) + implementation(Compose.coilHttp) } } diff --git a/richtext-markdown/src/androidMain/kotlin/com/halilibo/richtext/markdown/MarkdownImage.kt b/richtext-markdown/src/androidMain/kotlin/com/halilibo/richtext/markdown/MarkdownImage.kt index 64d8a883..d0c6db10 100644 --- a/richtext-markdown/src/androidMain/kotlin/com/halilibo/richtext/markdown/MarkdownImage.kt +++ b/richtext-markdown/src/androidMain/kotlin/com/halilibo/richtext/markdown/MarkdownImage.kt @@ -4,11 +4,11 @@ import android.annotation.SuppressLint import android.util.Base64 import androidx.compose.foundation.Image import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.BoxWithConstraintsScope import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.isSpecified @@ -16,9 +16,10 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp -import coil.compose.rememberAsyncImagePainter -import coil.request.ImageRequest -import coil.size.Size +import coil3.compose.rememberAsyncImagePainter +import coil3.request.ImageRequest +import coil3.request.crossfade +import coil3.size.Size private val DEFAULT_IMAGE_SIZE = 64.dp @@ -46,39 +47,10 @@ internal actual fun MarkdownImage( .build() ) - val density = LocalDensity.current - @SuppressLint("UnusedBoxWithConstraintsScope") BoxWithConstraints(modifier, contentAlignment = Alignment.Center) { - val sizeModifier by remember(density, painter) { - derivedStateOf { - val painterIntrinsicSize = painter.state.painter?.intrinsicSize - if (painterIntrinsicSize != null && - painterIntrinsicSize.isSpecified && - painterIntrinsicSize.width != Float.POSITIVE_INFINITY && - painterIntrinsicSize.height != Float.POSITIVE_INFINITY - ) { - val width = painterIntrinsicSize.width - val height = painterIntrinsicSize.height - val scale = if (width > constraints.maxWidth) { - constraints.maxWidth.toFloat() / width - } else { - 1f - } - - with(density) { - Modifier.size( - (width * scale).toDp(), - (height * scale).toDp() - ) - } - } else { - // if size is not defined at all, Coil fails to render the image - // here, we give a default size for images until they are loaded. - Modifier.size(DEFAULT_IMAGE_SIZE) - } - } - } + val painterState by painter.state.collectAsState() + val sizeModifier = renderInSize(painterState.painter?.intrinsicSize) Image( painter = painter, @@ -88,3 +60,37 @@ internal actual fun MarkdownImage( ) } } + +@Composable +public fun BoxWithConstraintsScope.renderInSize( + painterIntrinsicSize: androidx.compose.ui.geometry.Size?, +): Modifier { + val density = LocalDensity.current + + val sizeModifier = if (painterIntrinsicSize != null && + painterIntrinsicSize.isSpecified && + painterIntrinsicSize.width != Float.POSITIVE_INFINITY && + painterIntrinsicSize.height != Float.POSITIVE_INFINITY + ) { + val width = painterIntrinsicSize.width + val height = painterIntrinsicSize.height + val scale = if (width > constraints.maxWidth) { + constraints.maxWidth.toFloat() / width + } else { + 1f + } + + with(density) { + Modifier.size( + (width * scale).toDp(), + (height * scale).toDp() + ) + } + } else { + // if size is not defined at all, Coil fails to render the image + // here, we give a default size for images until they are loaded. + Modifier.size(DEFAULT_IMAGE_SIZE) + } + + return sizeModifier +} diff --git a/richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/FormattedList.kt b/richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/FormattedList.kt index 466e1138..37aa637b 100644 --- a/richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/FormattedList.kt +++ b/richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/FormattedList.kt @@ -100,7 +100,7 @@ public interface UnorderedMarkers { * Creates an [UnorderedMarkers] that will cycle through the values in [markers] for each * indentation level. */ -public fun @Composable RichTextScope.textUnorderedMarkers( +public fun RichTextScope.textUnorderedMarkers( vararg markers: String ): UnorderedMarkers = UnorderedMarkers { Text(markers[it % markers.size]) diff --git a/richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/string/RichTextString.kt b/richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/string/RichTextString.kt index 0c913d60..5a32426b 100644 --- a/richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/string/RichTextString.kt +++ b/richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/string/RichTextString.kt @@ -50,6 +50,26 @@ public class RichTextStringStyle( public val codeStyle: SpanStyle? = null, public val linkStyle: TextLinkStyles? = null ) { + public fun copy( + boldStyle: SpanStyle? = this.boldStyle, + italicStyle: SpanStyle? = this.italicStyle, + underlineStyle: SpanStyle? = this.underlineStyle, + strikethroughStyle: SpanStyle? = this.strikethroughStyle, + subscriptStyle: SpanStyle? = this.subscriptStyle, + superscriptStyle: SpanStyle? = this.superscriptStyle, + codeStyle: SpanStyle? = this.codeStyle, + linkStyle: TextLinkStyles? = this.linkStyle + ): RichTextStringStyle = RichTextStringStyle( + boldStyle = boldStyle, + italicStyle = italicStyle, + underlineStyle = underlineStyle, + strikethroughStyle = strikethroughStyle, + subscriptStyle = subscriptStyle, + superscriptStyle = superscriptStyle, + codeStyle = codeStyle, + linkStyle = linkStyle + ) + internal fun merge(otherStyle: RichTextStringStyle?): RichTextStringStyle { if (otherStyle == null) return this return RichTextStringStyle(