From 7aace941bd8a9cbcb9054dff0fcc7bccb22f15c7 Mon Sep 17 00:00:00 2001 From: Travis Wyatt Date: Sun, 30 Mar 2025 02:06:56 -0700 Subject: [PATCH] Create documentation module [skip ci] --- .github/workflows/gh-pages.yml | 5 +- documentation/README.md | 39 ++++ documentation/build.gradle.kts | 114 ++++++++++++ documentation/mermaid/.gitignore | 2 + documentation/mermaid/class_structure.mermaid | 25 +++ documentation/mermaid/convert.sh | 9 + documentation/mermaid/data_to_dom.mermaid | 2 + documentation/proguard-rules.pro | 109 +++++++++++ .../src/androidMain/AndroidManifest.xml | 19 ++ .../src/androidMain/kotlin/MainActivity.kt | 13 ++ .../drawable/class_structure.png | Bin 0 -> 34445 bytes .../composeResources/drawable/data_to_dom.png | Bin 0 -> 3414 bytes .../commonMain/composeResources/files/samples | 1 + .../font/roboto_mono_regular.ttf | Bin 0 -> 87236 bytes .../composeResources/values/strings.xml | 6 + documentation/src/commonMain/kotlin/App.kt | 32 ++++ .../src/commonMain/kotlin/AppNavHost.kt | 41 +++++ .../src/commonMain/kotlin/Hightlight.kt | 44 +++++ documentation/src/commonMain/kotlin/Screen.kt | 14 ++ .../commonMain/kotlin/components/AppBar.kt | 50 +++++ .../commonMain/kotlin/components/CodeView.kt | 33 ++++ .../commonMain/kotlin/components/Loading.kt | 19 ++ .../src/commonMain/kotlin/components/Quote.kt | 30 +++ .../samples/InteractiveTreeChartView.kt | 15 ++ .../kotlin/features/samples/SamplesScreen.kt | 70 +++++++ .../kotlin/features/samples/SineWaveScreen.kt | 55 ++++++ .../features/samples/SineWaveViewModel.kt | 28 +++ .../features/samples/TreeChartScreen.kt | 32 ++++ .../features/samples/TreeChartViewModel.kt | 19 ++ .../features/tutorial/TutorialScreen.kt | 173 ++++++++++++++++++ .../features/tutorial/TutorialViewModel.kt | 24 +++ .../kotlin/samples/InteractiveTreeChart.kt | 143 +++++++++++++++ .../kotlin/samples/MovingSineWaveView.kt | 23 +++ .../commonMain/kotlin/samples/data/Point.kt | 6 + .../kotlin/samples/data/SineWave.kt | 32 ++++ .../kotlin/samples/tutorial/BarChart.kt | 52 ++++++ .../kotlin/samples/tutorial/Line1.kt | 23 +++ .../kotlin/samples/tutorial/Line2.kt | 33 ++++ .../kotlin/samples/updaters/LineChart.kt | 117 ++++++++++++ .../src/commonMain/kotlin/theme/AppTheme.kt | 53 ++++++ .../kotlin/theme/LocalSyntaxTheme.kt | 9 + .../kotlin/theme/LocalTypography.kt | 7 + .../src/commonMain/kotlin/theme/Typography.kt | 16 ++ documentation/src/jsMain/kotlin/Main.js.kt | 22 +++ documentation/src/jsMain/resources/index.html | 13 ++ documentation/src/jvmMain/kotlin/Main.jvm.kt | 12 ++ .../src/macosMain/kotlin/Main.macos.kt | 28 +++ gradle/libs.versions.toml | 9 +- settings.gradle.kts | 1 + 49 files changed, 1618 insertions(+), 4 deletions(-) create mode 100644 documentation/README.md create mode 100644 documentation/build.gradle.kts create mode 100644 documentation/mermaid/.gitignore create mode 100644 documentation/mermaid/class_structure.mermaid create mode 100755 documentation/mermaid/convert.sh create mode 100644 documentation/mermaid/data_to_dom.mermaid create mode 100644 documentation/proguard-rules.pro create mode 100644 documentation/src/androidMain/AndroidManifest.xml create mode 100644 documentation/src/androidMain/kotlin/MainActivity.kt create mode 100644 documentation/src/commonMain/composeResources/drawable/class_structure.png create mode 100644 documentation/src/commonMain/composeResources/drawable/data_to_dom.png create mode 120000 documentation/src/commonMain/composeResources/files/samples create mode 100644 documentation/src/commonMain/composeResources/font/roboto_mono_regular.ttf create mode 100644 documentation/src/commonMain/composeResources/values/strings.xml create mode 100644 documentation/src/commonMain/kotlin/App.kt create mode 100644 documentation/src/commonMain/kotlin/AppNavHost.kt create mode 100644 documentation/src/commonMain/kotlin/Hightlight.kt create mode 100644 documentation/src/commonMain/kotlin/Screen.kt create mode 100644 documentation/src/commonMain/kotlin/components/AppBar.kt create mode 100644 documentation/src/commonMain/kotlin/components/CodeView.kt create mode 100644 documentation/src/commonMain/kotlin/components/Loading.kt create mode 100644 documentation/src/commonMain/kotlin/components/Quote.kt create mode 100644 documentation/src/commonMain/kotlin/features/samples/InteractiveTreeChartView.kt create mode 100644 documentation/src/commonMain/kotlin/features/samples/SamplesScreen.kt create mode 100644 documentation/src/commonMain/kotlin/features/samples/SineWaveScreen.kt create mode 100644 documentation/src/commonMain/kotlin/features/samples/SineWaveViewModel.kt create mode 100644 documentation/src/commonMain/kotlin/features/samples/TreeChartScreen.kt create mode 100644 documentation/src/commonMain/kotlin/features/samples/TreeChartViewModel.kt create mode 100644 documentation/src/commonMain/kotlin/features/tutorial/TutorialScreen.kt create mode 100644 documentation/src/commonMain/kotlin/features/tutorial/TutorialViewModel.kt create mode 100644 documentation/src/commonMain/kotlin/samples/InteractiveTreeChart.kt create mode 100644 documentation/src/commonMain/kotlin/samples/MovingSineWaveView.kt create mode 100644 documentation/src/commonMain/kotlin/samples/data/Point.kt create mode 100644 documentation/src/commonMain/kotlin/samples/data/SineWave.kt create mode 100644 documentation/src/commonMain/kotlin/samples/tutorial/BarChart.kt create mode 100644 documentation/src/commonMain/kotlin/samples/tutorial/Line1.kt create mode 100644 documentation/src/commonMain/kotlin/samples/tutorial/Line2.kt create mode 100644 documentation/src/commonMain/kotlin/samples/updaters/LineChart.kt create mode 100644 documentation/src/commonMain/kotlin/theme/AppTheme.kt create mode 100644 documentation/src/commonMain/kotlin/theme/LocalSyntaxTheme.kt create mode 100644 documentation/src/commonMain/kotlin/theme/LocalTypography.kt create mode 100644 documentation/src/commonMain/kotlin/theme/Typography.kt create mode 100644 documentation/src/jsMain/kotlin/Main.js.kt create mode 100644 documentation/src/jsMain/resources/index.html create mode 100644 documentation/src/jvmMain/kotlin/Main.jvm.kt create mode 100644 documentation/src/macosMain/kotlin/Main.macos.kt diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 779e624fe..1d4010de8 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -11,7 +11,6 @@ permissions: env: GRADLE_OPTS: -Dorg.gradle.daemon=false - JEKYLL_ENV: production jobs: deploy: @@ -33,8 +32,8 @@ jobs: with: cache-read-only: true - - run: ./gradlew -Pbaseurl='${{ steps.pages.outputs.base_path }}' :website:browserBuild + - run: ./gradlew :documentation:jsBrowserDistribution - uses: JamesIves/github-pages-deploy-action@v4 with: - folder: website/_site + folder: documentation/build/dist/js/productionExecutable diff --git a/documentation/README.md b/documentation/README.md new file mode 100644 index 000000000..029fd7b8f --- /dev/null +++ b/documentation/README.md @@ -0,0 +1,39 @@ +## Android + +```shell +./gradlew installDebug +``` + +## Web + +```shell +./gradlew jsBrowserDevelopmentRun +``` + +## Desktop (JVM) + +```shell +./gradlew run +``` + +## Desktop (macOS) + +```shell +./gradlew runDebugExecutableMacosArm64 +``` + +# Development + +## Resources + +### Charts + +Mermaid charts should be placed in the `mermaid/` directory with `.mermaid` extensions. +To convert the `*.mermaid` files (from the `mermaid` directory) to PNGs (in the +`src/commonMain/composeResources/drawable/` directory), run `convert.sh`. + +To generate resources and accessors run: + +```shell +./gradlew :documentation:generateComposeResClass :documentation:generateResourceAccessorsForCommonMain +``` diff --git a/documentation/build.gradle.kts b/documentation/build.gradle.kts new file mode 100644 index 000000000..a63223358 --- /dev/null +++ b/documentation/build.gradle.kts @@ -0,0 +1,114 @@ +import org.jetbrains.compose.desktop.application.dsl.TargetFormat + +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.compose) + alias(libs.plugins.compose.compiler) + alias(libs.plugins.kotlin.multiplatform) + alias(libs.plugins.serialization) +} + +kotlin { + jvmToolchain(libs.versions.jvm.toolchain.get().toInt()) + + androidTarget() + iosArm64 { + binaries.framework { + baseName = "DocumentationApp" + binaryOption("bundleId", "com.juul.krayon.documentation.ios") + binaryOption("bundleShortVersionString", "1.0.0") + binaryOption("bundleVersion", "1") + } + } + js { + outputModuleName = "documentation" + browser { + commonWebpackConfig { + outputFileName = "documentation.js" + } + } + binaries.executable() + } + jvm() + macosArm64 { + binaries { + executable { + entryPoint = "com.juul.krayon.documentation.main" + } + } + } + + sourceSets { + all { + languageSettings.optIn("kotlin.js.ExperimentalJsExport") + languageSettings.optIn("org.jetbrains.compose.resources.ExperimentalResourceApi") + } + + commonMain.dependencies { + implementation(compose.components.resources) + implementation(compose.foundation) + implementation(compose.material) + implementation(compose.runtime) + implementation(compose.ui) + implementation(libs.coroutines.core) + implementation(libs.highlights) + implementation(libs.markdown) + implementation(libs.navigation) + implementation(libs.serialization) + implementation(projects.box) + implementation(projects.compose) + } + + androidMain.dependencies { + implementation(libs.androidx.activity.compose) + implementation(libs.coroutines.android) + } + + jvmMain.dependencies { + implementation(compose.desktop.currentOs) + implementation(compose.preview) + implementation(libs.coroutines.swing) + } + } +} + +android { + namespace = "com.juul.krayon.documentation" + compileSdk = libs.versions.android.compile.get().toInt() + + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + sourceSets["main"].res.srcDirs("src/commonMain/composeResources") + sourceSets["main"].resources.srcDirs("src/commonMain/composeResources/files") + + defaultConfig { + applicationId = "com.juul.krayon.documentation" + minSdk = libs.versions.android.min.get().toInt() + targetSdk = libs.versions.android.target.get().toInt() + versionCode = 1 + versionName = "1.0" + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } + buildFeatures.compose = true +} + +compose { + desktop { + application { + mainClass = "com.juul.krayon.documentation.Main_jvmKt" + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "documentation" + packageVersion = "1.0.0" + } + + buildTypes.release.proguard { + version.set("7.4.0") + configurationFiles.from(project.file("proguard-rules.pro")) + } + } + } +} diff --git a/documentation/mermaid/.gitignore b/documentation/mermaid/.gitignore new file mode 100644 index 000000000..19fce76e2 --- /dev/null +++ b/documentation/mermaid/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +package*.json diff --git a/documentation/mermaid/class_structure.mermaid b/documentation/mermaid/class_structure.mermaid new file mode 100644 index 000000000..746f2c2bd --- /dev/null +++ b/documentation/mermaid/class_structure.mermaid @@ -0,0 +1,25 @@ +graph LR; + Data --> Elements + subgraph Krayon + Elements + SvgKanvas + subgraph Web + HtmlKanvas + end + subgraph Android + AndroidKanvas + ComposeKanvas + end + subgraph iOS + CGContextKanvas + end + end + Elements --> HtmlKanvas + Elements --> AndroidKanvas + Elements --> SvgKanvas + Elements --> ComposeKanvas + Elements --> CGContextKanvas + style Krayon fill:#b3e5fc + style Web fill:#e8d44d + style Android fill:#9fc137 + style iOS fill:#adadad diff --git a/documentation/mermaid/convert.sh b/documentation/mermaid/convert.sh new file mode 100755 index 000000000..8dfc36291 --- /dev/null +++ b/documentation/mermaid/convert.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +npm install @mermaid-js/mermaid-cli +for file in *.mermaid; do + ./node_modules/.bin/mmdc npx mmdc \ + --backgroundColor transparent \ + --input "$file" \ + --output "../src/commonMain/composeResources/drawable/${file%.mermaid}.png" +done diff --git a/documentation/mermaid/data_to_dom.mermaid b/documentation/mermaid/data_to_dom.mermaid new file mode 100644 index 000000000..a7db36809 --- /dev/null +++ b/documentation/mermaid/data_to_dom.mermaid @@ -0,0 +1,2 @@ +graph LR; + Data -- D3 --> DOM; diff --git a/documentation/proguard-rules.pro b/documentation/proguard-rules.pro new file mode 100644 index 000000000..e6780d51c --- /dev/null +++ b/documentation/proguard-rules.pro @@ -0,0 +1,109 @@ +-keep class com.google.gson.reflect.TypeToken +-keep class * extends com.google.gson.reflect.TypeToken +-keep public class * implements java.lang.reflect.Type + +-keep class data.io.** { *; } +-keep class base.** { *; } +-keep class kotlinx.coroutines.** { *; } + +-keep class com.google.firebase.** { *; } +-keep class com.google.android.gms.** { *; } +-keep class org.koin.** { *; } +-keep class org.koin.core.** { *; } +-keep class org.koin.dsl.** { *; } +-keep class io.ktor.utils.io.** { *; } +-keep class io.ktor.utils.io.jvm.** { *; } +-keep class io.ktor.utils.io.nio.** { *; } +-keep class io.ktor.server.config.** { *; } +-keep class io.ktor.serialization.** { *; } +-keep class kotlin.reflect.jvm.internal.** { *; } +-keep class coil3.** { *; } +-keep class androidx.datastore.preferences.** { *; } +-keep class androidx.collection.** { *; } +-keep class androidx.lifecycle.** { *; } +-keep class android.graphics.drawable.Drawable { *; } +-keep class android.graphics.Bitmap { *; } +-keep class android.graphics.Canvas { *; } +-keep class android.graphics.Shader$TileMode { *; } +-keep class android.graphics.Shader { *; } +-keep class android.graphics.Paint { *; } +-keep class android.graphics.BitmapShader { *; } +-keep class android.database.** { *; } +-keep class android.os.** { *; } +-keep class android.asynclayoutinflater.view.** { *; } +-keep class androidx.core.graphics.drawable.** { *; } +-keep class androidx.core.internal.view.** { *; } +-keep class kotlin.** { *; } +-keep class org.jetbrains.skia.** { *; } +-keep class org.jetbrains.skiko.** { *; } +-keep class android.view.** { *; } +-keep class org.example.project.NavigationNode + +-keepattributes SourceFile,LineNumberTable,Signature,*Annotation* + +-keepclasseswithmembers public class MainKt { + public static void main(java.lang.String[]); +} + +-assumenosideeffects public class androidx.compose.runtime.ComposerKt { + void sourceInformation(androidx.compose.runtime.Composer,java.lang.String); + void sourceInformationMarkerStart(androidx.compose.runtime.Composer,int,java.lang.String); + void sourceInformationMarkerEnd(androidx.compose.runtime.Composer); +} + +# caused crashes on release Jvm +#-dontwarn androidx.compose.material.** +#-keep class androidx.compose.material3.** { *; } +#-keep class androidx.compose.runtime.** { *; } + +# Keep `INSTANCE.serializer()` of serializable objects. +-if @kotlinx.serialization.Serializable class ** { + public static ** INSTANCE; +} +-keepclassmembers class <1> { + public static <1> INSTANCE; + kotlinx.serialization.KSerializer serializer(...); +} +# Keep `Companion` object fields of serializable classes. +# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects. +-if @kotlinx.serialization.Serializable class ** +-keepclassmembers class <1> { + static <1>$Companion Companion; +} + +# Keep `serializer()` on companion objects (both default and named) of serializable classes. +-if @kotlinx.serialization.Serializable class ** { + static **$* *; +} +-keepclassmembers class <2>$<3> { + kotlinx.serialization.KSerializer serializer(...); +} + +# Keep `INSTANCE.serializer()` of serializable objects. +-if @kotlinx.serialization.Serializable class ** { + public static ** INSTANCE; +} +-keepclassmembers class <1> { + public static <1> INSTANCE; + kotlinx.serialization.KSerializer serializer(...); +} + +# @Serializable and @Polymorphic are used at runtime for polymorphic serialization. +-keepattributes RuntimeVisibleAnnotations,AnnotationDefault + +# Don't print notes about potential mistakes or omissions in the configuration for kotlinx-serialization classes +# See also https://github.com/Kotlin/kotlinx.serialization/issues/1900 +-dontnote kotlinx.serialization.** + +# Serialization core uses `java.lang.ClassValue` for caching inside these specified classes. +# If there is no `java.lang.ClassValue` (for example, in Android), then R8/ProGuard will print a warning. +# However, since in this case they will not be used, we can disable these warnings +-dontwarn kotlinx.serialization.internal.ClassValueReferences + +# disable optimisation for descriptor field because in some versions of ProGuard, optimization generates incorrect bytecode that causes a verification error +# see https://github.com/Kotlin/kotlinx.serialization/issues/2719 +-keepclassmembers public class **$$serializer { + private ** descriptor; +} + +-ignorewarnings diff --git a/documentation/src/androidMain/AndroidManifest.xml b/documentation/src/androidMain/AndroidManifest.xml new file mode 100644 index 000000000..a3cc5bbdd --- /dev/null +++ b/documentation/src/androidMain/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/documentation/src/androidMain/kotlin/MainActivity.kt b/documentation/src/androidMain/kotlin/MainActivity.kt new file mode 100644 index 000000000..710babba1 --- /dev/null +++ b/documentation/src/androidMain/kotlin/MainActivity.kt @@ -0,0 +1,13 @@ +package com.juul.krayon.documentation + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent + +class MainActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { App() } + } +} diff --git a/documentation/src/commonMain/composeResources/drawable/class_structure.png b/documentation/src/commonMain/composeResources/drawable/class_structure.png new file mode 100644 index 0000000000000000000000000000000000000000..9fe21bb406d899bb4984bbeb6388273d3573d51d GIT binary patch literal 34445 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
peN~| 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 literal 0 HcmV?d00001 diff --git a/documentation/src/commonMain/composeResources/drawable/data_to_dom.png b/documentation/src/commonMain/composeResources/drawable/data_to_dom.png new file mode 100644 index 0000000000000000000000000000000000000000..d6abafcb266c6df35981c7df54083cc0e8617bd8 GIT binary patch literal 3414 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 WiD&}1U9w};%2Kl9!gJKh^8W$jCY;;= literal 0 HcmV?d00001 diff --git a/documentation/src/commonMain/composeResources/values/strings.xml b/documentation/src/commonMain/composeResources/values/strings.xml new file mode 100644 index 000000000..0c47cddb8 --- /dev/null +++ b/documentation/src/commonMain/composeResources/values/strings.xml @@ -0,0 +1,6 @@ + + Krayon: Documentation + Tutorial + Samples + Chart Sample + diff --git a/documentation/src/commonMain/kotlin/App.kt b/documentation/src/commonMain/kotlin/App.kt new file mode 100644 index 000000000..3969be7ad --- /dev/null +++ b/documentation/src/commonMain/kotlin/App.kt @@ -0,0 +1,32 @@ +package com.juul.krayon.documentation + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import androidx.navigation.compose.rememberNavController +import com.juul.krayon.documentation.components.AppBar +import com.juul.krayon.documentation.theme.AppTheme + +@Composable +fun App(navController: NavHostController = rememberNavController()) { + AppTheme { + Scaffold( + topBar = { AppBar(navController) }, + ) { + Box(contentAlignment = Alignment.TopCenter, modifier = Modifier.fillMaxSize()) { + Column(modifier = Modifier.width(1000.dp).verticalScroll(rememberScrollState())) { + AppNavHost(navController) + } + } + } + } +} diff --git a/documentation/src/commonMain/kotlin/AppNavHost.kt b/documentation/src/commonMain/kotlin/AppNavHost.kt new file mode 100644 index 000000000..41f67b079 --- /dev/null +++ b/documentation/src/commonMain/kotlin/AppNavHost.kt @@ -0,0 +1,41 @@ +package com.juul.krayon.documentation + +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import com.juul.krayon.documentation.features.samples.SamplesScreen +import com.juul.krayon.documentation.features.tutorial.TutorialScreen +import com.juul.krayon.documentation.features.samples.SineWaveScreen +import com.juul.krayon.documentation.features.samples.TreeChartScreen + +@Composable +fun AppNavHost(navController: NavHostController) { + NavHost( + navController, + startDestination = Screen.Tutorial.name, + modifier = Modifier.padding(16.dp), + ) { + composable(route = Screen.Tutorial.name) { + TutorialScreen() + } + + composable(route = Screen.Samples.name) { + SamplesScreen( + onSineWaveClick = { navController.navigate(Screen.SineWaveSample.name) }, + onTreeChartClick = { navController.navigate(Screen.TreeChartSample.name) } + ) + } + + composable(route = Screen.SineWaveSample.name) { + SineWaveScreen() + } + + composable(route = Screen.TreeChartSample.name) { + TreeChartScreen() + } + } +} diff --git a/documentation/src/commonMain/kotlin/Hightlight.kt b/documentation/src/commonMain/kotlin/Hightlight.kt new file mode 100644 index 000000000..dec67ad5e --- /dev/null +++ b/documentation/src/commonMain/kotlin/Hightlight.kt @@ -0,0 +1,44 @@ +package com.juul.krayon.documentation + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import dev.snipme.highlights.Highlights +import dev.snipme.highlights.model.BoldHighlight +import dev.snipme.highlights.model.ColorHighlight +import dev.snipme.highlights.model.SyntaxLanguage +import dev.snipme.highlights.model.SyntaxLanguage.KOTLIN +import dev.snipme.highlights.model.SyntaxTheme + +fun String.highlight( + theme: SyntaxTheme, + language: SyntaxLanguage = KOTLIN, +): AnnotatedString { + val highlights = Highlights.Builder() + .code(this) + .theme(theme) + .language(language) + .build() + .getHighlights() + + return buildAnnotatedString { + append(this@highlight) + highlights.forEach { + when (it) { + is BoldHighlight -> addStyle( + SpanStyle(fontWeight = FontWeight.Bold), + start = it.location.start, + end = it.location.end, + ) + + is ColorHighlight -> addStyle( + SpanStyle(color = Color(it.rgb).copy(alpha = 1f)), + start = it.location.start, + end = it.location.end, + ) + } + } + } +} diff --git a/documentation/src/commonMain/kotlin/Screen.kt b/documentation/src/commonMain/kotlin/Screen.kt new file mode 100644 index 000000000..66ed76043 --- /dev/null +++ b/documentation/src/commonMain/kotlin/Screen.kt @@ -0,0 +1,14 @@ +package com.juul.krayon.documentation + +import com.juul.krayon.documentation.generated.resources.Res +import com.juul.krayon.documentation.generated.resources.chart_sample_title +import com.juul.krayon.documentation.generated.resources.samples_title +import com.juul.krayon.documentation.generated.resources.tutorial_title +import org.jetbrains.compose.resources.StringResource + +enum class Screen(val title: StringResource) { + Tutorial(Res.string.tutorial_title), + Samples(Res.string.samples_title), + SineWaveSample(Res.string.chart_sample_title), + TreeChartSample(Res.string.chart_sample_title), +} diff --git a/documentation/src/commonMain/kotlin/components/AppBar.kt b/documentation/src/commonMain/kotlin/components/AppBar.kt new file mode 100644 index 000000000..4a76f1b31 --- /dev/null +++ b/documentation/src/commonMain/kotlin/components/AppBar.kt @@ -0,0 +1,50 @@ +package com.juul.krayon.documentation.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import androidx.navigation.compose.currentBackStackEntryAsState +import com.juul.krayon.documentation.Screen + +@Composable +fun AppBar(navController: NavHostController) { + val currentBackStackEntry by navController.currentBackStackEntryAsState() + TopAppBar( + title = {}, + actions = { + Actions { + navController.navigate(it) { + launchSingleTop = true + currentBackStackEntry?.destination?.route?.apply { + popUpTo(this) { inclusive = true } + } + } + } + }, + ) +} + +@Composable +private fun Actions( + onNavigateToRoute: (route: String) -> Unit, +) { + Text( + text = "Tutorial", + modifier = Modifier.clickable { + onNavigateToRoute(Screen.Tutorial.name) + }.padding(10.dp), + ) + + Text( + text = "Samples", + modifier = Modifier.clickable { + onNavigateToRoute(Screen.Samples.name) + }.padding(10.dp), + ) +} diff --git a/documentation/src/commonMain/kotlin/components/CodeView.kt b/documentation/src/commonMain/kotlin/components/CodeView.kt new file mode 100644 index 000000000..0ee08f6ca --- /dev/null +++ b/documentation/src/commonMain/kotlin/components/CodeView.kt @@ -0,0 +1,33 @@ +package com.juul.krayon.documentation.components + +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.text.AnnotatedString +import com.juul.krayon.documentation.generated.resources.Res +import com.juul.krayon.documentation.highlight +import com.juul.krayon.documentation.theme.AppTheme +import dev.snipme.highlights.model.SyntaxTheme +import dev.snipme.highlights.model.SyntaxThemes + +@Composable +fun CodeView(path: String) { + val highlight = AppTheme.highlight + var text by remember(path) { mutableStateOf(null) } + LaunchedEffect(Unit) { + text = load("files/samples/$path", highlight) + } + + text?.let { + Text(it, style = AppTheme.typography.code) + } +} + +private suspend fun load( + path: String, + theme: SyntaxTheme = SyntaxThemes.default(), +): AnnotatedString = Res.readBytes(path).decodeToString().highlight(theme) diff --git a/documentation/src/commonMain/kotlin/components/Loading.kt b/documentation/src/commonMain/kotlin/components/Loading.kt new file mode 100644 index 000000000..10e67bde0 --- /dev/null +++ b/documentation/src/commonMain/kotlin/components/Loading.kt @@ -0,0 +1,19 @@ +package com.juul.krayon.documentation.components + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.width +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment.Companion.Center +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun Loading() { + Box(contentAlignment = Center, modifier = Modifier.fillMaxSize()) { + CircularProgressIndicator( + modifier = Modifier.width(64.dp), + ) + } +} diff --git a/documentation/src/commonMain/kotlin/components/Quote.kt b/documentation/src/commonMain/kotlin/components/Quote.kt new file mode 100644 index 000000000..8a978cb0d --- /dev/null +++ b/documentation/src/commonMain/kotlin/components/Quote.kt @@ -0,0 +1,30 @@ +package com.juul.krayon.documentation.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +@Composable +fun Quote( + width: Dp = 2.dp, + color: Color = MaterialTheme.colors.onSurface, + content: @Composable () -> Unit, +) { + Row(Modifier.height(IntrinsicSize.Max)) { + Box(Modifier.fillMaxHeight().width(width).background(color)) + Spacer(Modifier.size(5.dp)) + content() + } +} diff --git a/documentation/src/commonMain/kotlin/features/samples/InteractiveTreeChartView.kt b/documentation/src/commonMain/kotlin/features/samples/InteractiveTreeChartView.kt new file mode 100644 index 000000000..face70295 --- /dev/null +++ b/documentation/src/commonMain/kotlin/features/samples/InteractiveTreeChartView.kt @@ -0,0 +1,15 @@ +package com.juul.krayon.documentation.features.samples + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import com.juul.krayon.compose.ElementView +import com.juul.krayon.documentation.samples.interactiveTreeChart + +@Composable +fun InteractiveTreeChartView( + modifier: Modifier = Modifier, +) { + val (flow, update) = remember { interactiveTreeChart() } + ElementView(dataSource = flow, updateElements = update, modifier) +} diff --git a/documentation/src/commonMain/kotlin/features/samples/SamplesScreen.kt b/documentation/src/commonMain/kotlin/features/samples/SamplesScreen.kt new file mode 100644 index 000000000..fb4ccdcc6 --- /dev/null +++ b/documentation/src/commonMain/kotlin/features/samples/SamplesScreen.kt @@ -0,0 +1,70 @@ +package com.juul.krayon.documentation.features.samples + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.Card +import androidx.compose.material.FloatingActionButton +import androidx.compose.material.Icon +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment.Companion.BottomEnd +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.juul.krayon.compose.ElementView +import com.juul.krayon.documentation.samples.MovingSineWaveView +import com.juul.krayon.documentation.samples.interactiveTreeChart + +@Composable +fun SamplesScreen( + onSineWaveClick: () -> Unit, + onTreeChartClick: () -> Unit, +) { + Row(modifier = Modifier.height(300.dp)) { + CodeCard( + Modifier.weight(1f), + onPlayClick = onSineWaveClick, + ) { + MovingSineWaveView(Modifier.fillMaxSize()) + } + + Spacer(modifier = Modifier.size(8.dp)) + + CodeCard( + Modifier.weight(1f), + onPlayClick = onTreeChartClick, + ) { + val (flow, update) = remember { interactiveTreeChart() } + ElementView( + dataSource = flow, + updateElements = update, + Modifier.fillMaxSize(), + ) + } + } +} + +@Composable +private fun CodeCard( + modifier: Modifier = Modifier, + onPlayClick: () -> Unit, + content: @Composable () -> Unit, +) { + Card(modifier) { + Box(modifier = Modifier.padding(8.dp)) { + content() + FloatingActionButton( + onClick = onPlayClick, + modifier = Modifier.align(BottomEnd).padding(5.dp), + ) { + Icon(Icons.Filled.PlayArrow, "View") + } + } + } +} diff --git a/documentation/src/commonMain/kotlin/features/samples/SineWaveScreen.kt b/documentation/src/commonMain/kotlin/features/samples/SineWaveScreen.kt new file mode 100644 index 000000000..8f3e52037 --- /dev/null +++ b/documentation/src/commonMain/kotlin/features/samples/SineWaveScreen.kt @@ -0,0 +1,55 @@ +package com.juul.krayon.documentation.features.samples + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.juul.krayon.documentation.features.samples.SineWaveViewModel.Code +import com.juul.krayon.documentation.highlight +import com.juul.krayon.documentation.samples.MovingSineWaveView +import com.juul.krayon.documentation.theme.AppTheme +import dev.snipme.highlights.model.SyntaxTheme + +@Composable +fun SineWaveScreen( + viewModel: SineWaveViewModel = viewModel { SineWaveViewModel() }, +) { + val code = viewModel.code.collectAsState().value + if (code == null) { + // Loading + } else { + Column { + MovingSineWaveView(Modifier.fillMaxWidth().height(300.dp)) + + val syntaxTheme = AppTheme.highlight + val (data, updater, glue) = remember(syntaxTheme) { code.highlight(syntaxTheme) } + + Text("Data") + Text(data, style = AppTheme.typography.code) + Text("Updater") + Text(updater, style = AppTheme.typography.code) + Text("Glue") + Text(glue, style = AppTheme.typography.code) + } + } +} + +private data class HighlightedCode( + val data: AnnotatedString, + val updater: AnnotatedString, + val glue: AnnotatedString, +) + +private fun Code.highlight(syntaxTheme: SyntaxTheme) = + HighlightedCode( + data.highlight(syntaxTheme), + updater.highlight(syntaxTheme), + glue.highlight(syntaxTheme), + ) diff --git a/documentation/src/commonMain/kotlin/features/samples/SineWaveViewModel.kt b/documentation/src/commonMain/kotlin/features/samples/SineWaveViewModel.kt new file mode 100644 index 000000000..6f3d24b30 --- /dev/null +++ b/documentation/src/commonMain/kotlin/features/samples/SineWaveViewModel.kt @@ -0,0 +1,28 @@ +package com.juul.krayon.documentation.features.samples + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.juul.krayon.documentation.generated.resources.Res +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch + +class SineWaveViewModel : ViewModel() { + + data class Code( + val data: String, + val updater: String, + val glue: String, + ) + + val code = MutableStateFlow(null) + + init { + viewModelScope.launch { + code.value = Code( + data = Res.readBytes("files/samples/data/SineWave.kt").decodeToString(), + updater = Res.readBytes("files/samples/updaters/LineChart.kt").decodeToString(), + glue = Res.readBytes("files/samples/MovingSineWaveView.kt").decodeToString(), + ) + } + } +} diff --git a/documentation/src/commonMain/kotlin/features/samples/TreeChartScreen.kt b/documentation/src/commonMain/kotlin/features/samples/TreeChartScreen.kt new file mode 100644 index 000000000..cade1704d --- /dev/null +++ b/documentation/src/commonMain/kotlin/features/samples/TreeChartScreen.kt @@ -0,0 +1,32 @@ +package com.juul.krayon.documentation.features.samples + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.juul.krayon.documentation.components.Loading +import com.juul.krayon.documentation.highlight +import com.juul.krayon.documentation.theme.AppTheme + +@Composable +fun TreeChartScreen( + viewModel: TreeChartViewModel = viewModel { TreeChartViewModel() }, +) { + val code = viewModel.code.collectAsState().value + if (code == null) { + Loading() + } else { + val syntaxTheme = AppTheme.highlight + val text = remember(syntaxTheme) { code.highlight(syntaxTheme) } + Column { + InteractiveTreeChartView(Modifier.fillMaxWidth().height(300.dp)) + Text(text) + } + } +} diff --git a/documentation/src/commonMain/kotlin/features/samples/TreeChartViewModel.kt b/documentation/src/commonMain/kotlin/features/samples/TreeChartViewModel.kt new file mode 100644 index 000000000..a0f6527bb --- /dev/null +++ b/documentation/src/commonMain/kotlin/features/samples/TreeChartViewModel.kt @@ -0,0 +1,19 @@ +package com.juul.krayon.documentation.features.samples + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.juul.krayon.documentation.generated.resources.Res +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch + +class TreeChartViewModel : ViewModel() { + + val code = MutableStateFlow(null) + + init { + viewModelScope.launch { + code.value = + Res.readBytes("files/samples/InteractiveTreeChart.kt").decodeToString() + } + } +} diff --git a/documentation/src/commonMain/kotlin/features/tutorial/TutorialScreen.kt b/documentation/src/commonMain/kotlin/features/tutorial/TutorialScreen.kt new file mode 100644 index 000000000..f59e73868 --- /dev/null +++ b/documentation/src/commonMain/kotlin/features/tutorial/TutorialScreen.kt @@ -0,0 +1,173 @@ +package com.juul.krayon.documentation.features.tutorial + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Column +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import androidx.compose.ui.layout.FixedScale +import androidx.compose.ui.text.AnnotatedString +import androidx.lifecycle.viewmodel.compose.viewModel +import com.juul.krayon.documentation.components.Loading +import com.juul.krayon.documentation.components.Quote +import com.juul.krayon.documentation.features.tutorial.TutorialViewModel.Code +import com.juul.krayon.documentation.generated.resources.Res +import com.juul.krayon.documentation.generated.resources.class_structure +import com.juul.krayon.documentation.generated.resources.data_to_dom +import com.juul.krayon.documentation.highlight +import com.juul.krayon.documentation.theme.AppTheme +import dev.snipme.highlights.model.SyntaxTheme +import org.jetbrains.compose.resources.painterResource + +@Composable +fun TutorialScreen( + viewModel: TutorialViewModel = viewModel { TutorialViewModel() }, +) { + val code = viewModel.code.collectAsState(null).value + if (code == null) { + Loading() + } else { + val syntaxTheme = AppTheme.highlight + Tutorial(remember(syntaxTheme) { code.highlight(syntaxTheme) }) + } +} + +private data class HighlightedCode( + val line1: AnnotatedString, +) + +private fun Code.highlight(syntaxTheme: SyntaxTheme) = + HighlightedCode( + line1 = line1.highlight(syntaxTheme), + ) + +@Composable +private fun Tutorial(code: HighlightedCode) { + Column { + Text("Comparison to [D3]", style = MaterialTheme.typography.h2) + Quote { + Text("D3 (or D3.js) is a free, open-source JavaScript library for visualizing data.") + } + Text("D3 (which stands for \"Data-Driven Documents\") is commonly used to have a dataset drive manipulation of the HTML [Document Object Model] (DOM).") + Image( + painterResource(Res.drawable.data_to_dom), + "Data to DOM", + contentScale = FixedScale(2f), + ) + Text("[D3]'s [\"most central feature\" is \"data binding\"](https://stackoverflow.com/a/50143500), whereas data can drive the mutation of the HTML [DOM]. [D3] can also be used to draw to SVG or [Canvas], but faux elements (rather than HTML [DOM] elements) are created to take advantage of [D3]'s data binding capabilities.") + Text("[D3] is undoubtedly a powerful visualization library, _but_ it only works in JavaScript environments (namely, the Web).") + Text("Enter Krayon, a [Kotlin]™ [multiplatform] library that aims to bring powerful visualization tools to the many platforms supported by [Kotlin]™ (e.g. Android, iOS, Web).") + Text("Elements", style = MaterialTheme.typography.h2) + Text("Since Android and iOS don't have an HTML [DOM], Krayon provides intermediary [`Element`]s that can undergo data binding and be rendered on supported platforms.") + Image( + painterResource(Res.drawable.class_structure), + "Class structure", + contentScale = FixedScale(2f), + ) + Text("Drawing", style = MaterialTheme.typography.h2) + Text("To draw a simple line on an HTML [Canvas], we start by creating a [Canvas]:") + Text( + """ + + """.trimIndent(), + style = AppTheme.typography.code, + ) + Text("We can then perform the following in [Kotlin]™:") + Text("1. Create a [`RootElement`]") + Text("2. Access [`RootElement`] as a [`Selection`]") + Text("3. Add a [`LineElement`] to the [`RootElement`]") + Text("4. Update the start and end points of the [`LineElement`]") + Text("5. Draw the [`RootElement`] to a [`HtmlKanvas`] (which bridges to an HTML [Canvas])") + Text(code.line1, style = AppTheme.typography.code) + } +} + +private val markdown = """ + We can simply call into the JavaScript API produced by Kotlin (via `@JsExport`): + + ```html + + ``` + + This will render as: + + + + + ### Data + + To draw the same line as above, but powered by a dataset, we can perform the following in + [Kotlin]™: + + 1. Create a [`RootElement`] + 2. Access [`RootElement`] as a [`Selection`] + 3. Selects all [`LineElement`]s that are children of the [`RootElement`] + 4. Associate data with the [`Selection`] + 5. Combine each element of the data with a [`LineElement`] + 6. Iterate over each item of the data (`data` is a `Pair`) and [`LineElement`]s + 7. Deconstruct `data` as `start` (`Point`) and `end` (`Point`) + 8. Assign the `Point` values to the [`LineElement`] properties (`startX`, `startY`, etc.) + 9. Draw the [`RootElement`] to a [`HtmlKanvas`] (which bridges to an HTML [Canvas]) + + ```kotlin + {% include kotlin/tutorial/Line2.kt %} + ``` + + This will render as: + + + + + ### Bar Chart + + We can use a larger dataset with the [`LineElement`] to create a simple bar chart. We start by + creating a larger HTML [Canvas]: + + ```html + + ``` + + Then we can construct the bar chart in [Kotlin]™: + + 1. Extract the `width` and `height` of the HTML [Canvas] as `Float`s + 2. Create an example dataset (1 through 10, incrementing by 1) + 3. Configure scaling on the x-axis (essentially, mapping dataset indices to the width of the [Canvas]) + 4. Configure scaling on the y-axis (mapping the range of dataset values to the height of the [Canvas])1 + 5. Configure the color and width of the bars + 6. Create a [`RootElement`] + 7. Access [`RootElement`] as a [`Selection`] + 8. Selects all [`LineElement`]s that are children of the [`RootElement`] + 9. Associate data with the [`Selection`] + 10. Combine each element of the data with a [`LineElement`] + 11. Iterate over each item of the data (indexed) + 12. Assign scaled values to the [`LineElement`] properties (`startX`, `startY`, etc.) + 13. Assign the bar paint (from step 5) + 14. Draw the [`RootElement`] to a [`HtmlKanvas`] (which bridges to an HTML [Canvas]) + + 1 Note that for step 4 the order of the `range` values are flipped (with `height` listed + before `0f`). This is to invert y-axis rendering, since the rendering origin is the upper left + corner (with y increasing downward). + + ```kotlin + {% include kotlin/tutorial/BarChart.kt %} + ``` + + This will render as: + + + + + [Canvas]: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API + [D3]: https://d3js.org/ + [DOM]: https://en.wikipedia.org/wiki/Document_Object_Model + [Document Object Model]: https://en.wikipedia.org/wiki/Document_Object_Model + [Kotlin]: https://kotlinlang.org/ + [`Element`]: https://juullabs.github.io/krayon/api/element/com.juul.krayon.element/-element/index.html + [`HtmlKanvas`]: https://juullabs.github.io/krayon/api/kanvas/com.juul.krayon.kanvas/-html-kanvas/index.html + [`LineElement`]: https://juullabs.github.io/krayon/api/element/com.juul.krayon.element/-line-element/index.html + [`RootElement`]: https://juullabs.github.io/krayon/api/element/com.juul.krayon.element/-root-element/index.html + [`Selection`]: https://juullabs.github.io/krayon/api/selection/com.juul.krayon.selection/-selection/index.html + [multiplatform]: https://kotlinlang.org/docs/multiplatform.html +""".trimIndent() diff --git a/documentation/src/commonMain/kotlin/features/tutorial/TutorialViewModel.kt b/documentation/src/commonMain/kotlin/features/tutorial/TutorialViewModel.kt new file mode 100644 index 000000000..d3f203bc2 --- /dev/null +++ b/documentation/src/commonMain/kotlin/features/tutorial/TutorialViewModel.kt @@ -0,0 +1,24 @@ +package com.juul.krayon.documentation.features.tutorial + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.juul.krayon.documentation.generated.resources.Res +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch + +class TutorialViewModel : ViewModel() { + + data class Code( + val line1: String, + ) + + val code = MutableStateFlow(null) + + init { + viewModelScope.launch { + code.value = Code( + line1 = Res.readBytes("files/samples/tutorial/Line1.kt").decodeToString(), + ) + } + } +} diff --git a/documentation/src/commonMain/kotlin/samples/InteractiveTreeChart.kt b/documentation/src/commonMain/kotlin/samples/InteractiveTreeChart.kt new file mode 100644 index 000000000..3bc91b8b5 --- /dev/null +++ b/documentation/src/commonMain/kotlin/samples/InteractiveTreeChart.kt @@ -0,0 +1,143 @@ +package com.juul.krayon.documentation.samples + +import com.juul.krayon.box.setShapeFrom +import com.juul.krayon.color.Color +import com.juul.krayon.color.crimson +import com.juul.krayon.color.darkBlue +import com.juul.krayon.color.forestGreen +import com.juul.krayon.color.lerp +import com.juul.krayon.color.white +import com.juul.krayon.element.ClickHandler +import com.juul.krayon.element.HoverHandler +import com.juul.krayon.element.RectangleElement +import com.juul.krayon.element.RootElement +import com.juul.krayon.element.TextElement +import com.juul.krayon.element.UpdateElement +import com.juul.krayon.hierarchy.flatHierarchy +import com.juul.krayon.hierarchy.removeHierarchy +import com.juul.krayon.hierarchy.sum +import com.juul.krayon.hierarchy.treemap.Treemap +import com.juul.krayon.hierarchy.treemap.layoutWith +import com.juul.krayon.kanvas.Font +import com.juul.krayon.kanvas.Paint +import com.juul.krayon.kanvas.Paint.Fill +import com.juul.krayon.kanvas.Paint.Text +import com.juul.krayon.kanvas.Paint.Text.Alignment.Center +import com.juul.krayon.kanvas.sansSerif +import com.juul.krayon.scale.max +import com.juul.krayon.scale.min +import com.juul.krayon.selection.append +import com.juul.krayon.selection.asSelection +import com.juul.krayon.selection.data +import com.juul.krayon.selection.each +import com.juul.krayon.selection.join +import com.juul.krayon.selection.selectAll +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlin.random.Random + +enum class Letter { + Alpha, + Beta, + Gamma, + Delta, + Epsilon, + Zeta, + Eta, + Theta, + Iota, + Kappa, +} + +data class InteractiveTreeChart( + val selection: Letter?, + val hovered: Letter?, + val counts: Map, +) + +internal fun interactiveTreeChart(): Pair, UpdateElement> { + val selectionState = MutableStateFlow(null as Letter?) + val hoveredState = MutableStateFlow(null as Letter?) + val letterSizes = Letter.entries.associateWith { Random.nextInt(50, 500) } + val dataFlow = combine(selectionState, hoveredState) { selection, hovered -> + InteractiveTreeChart(selection, hovered, letterSizes) + } + val updater = UpdateElement { root, width, height, data -> + updateInteractiveTreeChart( + root, + width, + height, + data, + clickHandler = { letter -> selectionState.value = letter.takeUnless { it == selectionState.value } }, + hoverHandler = { letter, hovered -> + if (!hovered && hoveredState.value == letter) { + hoveredState.value = null + } else if (hovered) { + hoveredState.value = letter + } + }, + ) + } + return dataFlow to updater +} + +private fun updateInteractiveTreeChart( + root: RootElement, + width: Float, + height: Float, + data: InteractiveTreeChart, + clickHandler: ClickHandler, + hoverHandler: HoverHandler, +) { + val min = data.counts.values.min { it } + val max = data.counts.values.max { it } + + fun colorFor(letter: Letter): Color { + val value = data.counts[letter] ?: 0 + val baseColor = if (letter == data.selection) { + forestGreen + } else { + lerp(crimson, darkBlue, (value - min).toFloat() / (max - min)) + } + return if (letter == data.hovered) { + lerp(baseColor, white, 0.25f) + } else { + baseColor + } + } + + val lettersToTiles = flatHierarchy(data.counts.entries) + .sum { it?.value?.toFloat() ?: 0f } + .layoutWith(Treemap(width, height)) + .removeHierarchy() + .toList() + + root.asSelection() + .selectAll(RectangleElement) + .data(lettersToTiles) + .join { append(RectangleElement) } + .each { (d) -> + val (entry, tile) = d + setShapeFrom(tile) + paint = Paint.FillAndStroke( + Fill(colorFor(entry.key)), + Paint.Stroke(white, 2f), + ) + onClick { clickHandler.onClick(entry.key) } + onHoverChanged { _, hovered -> hoverHandler.onHoverChanged(entry.key, hovered) } + } + + val textPaint = Text(white, size = 12f, alignment = Center, Font(sansSerif)) + root.asSelection() + .selectAll(TextElement) + .data(lettersToTiles) + .join { append(TextElement) } + .each { (d) -> + val (entry, tile) = d + x = tile.centerX + y = tile.centerY + textPaint.size * 0.75f + text = entry.key.name + paint = textPaint + } +} diff --git a/documentation/src/commonMain/kotlin/samples/MovingSineWaveView.kt b/documentation/src/commonMain/kotlin/samples/MovingSineWaveView.kt new file mode 100644 index 000000000..8dfd3c267 --- /dev/null +++ b/documentation/src/commonMain/kotlin/samples/MovingSineWaveView.kt @@ -0,0 +1,23 @@ +package com.juul.krayon.documentation.samples + +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import com.juul.krayon.compose.ElementView +import com.juul.krayon.documentation.samples.data.movingSineWave +import com.juul.krayon.documentation.samples.updaters.lineChart + +@Composable +fun MovingSineWaveView( + modifier: Modifier = Modifier, +) { + val colors = MaterialTheme.colors + ElementView( + dataSource = remember { movingSineWave() }, + updateElements = { root, width, height, data -> + lineChart(root, width, height, data, colors) + }, + modifier, + ) +} diff --git a/documentation/src/commonMain/kotlin/samples/data/Point.kt b/documentation/src/commonMain/kotlin/samples/data/Point.kt new file mode 100644 index 000000000..9f7aec46a --- /dev/null +++ b/documentation/src/commonMain/kotlin/samples/data/Point.kt @@ -0,0 +1,6 @@ +package com.juul.krayon.documentation.samples.data + +data class Point( + val x: Float, + val y: Float, +) diff --git a/documentation/src/commonMain/kotlin/samples/data/SineWave.kt b/documentation/src/commonMain/kotlin/samples/data/SineWave.kt new file mode 100644 index 000000000..ce6833825 --- /dev/null +++ b/documentation/src/commonMain/kotlin/samples/data/SineWave.kt @@ -0,0 +1,32 @@ +package com.juul.krayon.documentation.samples.data + +import kotlin.math.PI +import kotlin.math.sin +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds +import kotlin.time.TimeSource +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.isActive + +/** A flow that emits data for a continuously rolling sine wave. By default, completes a full cycle in 10 seconds. */ +internal fun movingSineWave( + period: Duration = 10.seconds, + samples: Int = 50, +): Flow> = flow { + var offset = 0f + var time = TimeSource.Monotonic.markNow() + while (currentCoroutineContext().isActive) { + emit(sineWave(offset, samples)) + offset += ((time.elapsedNow() / period) * 2 * PI).toFloat() + time = TimeSource.Monotonic.markNow() + } +} + +internal fun sineWave(offset: Float = 0f, samples: Int = 50): List = + (0 until samples).map { i -> + val proportion = i.toFloat() / (samples - 1) + val x = offset + (2 * PI * proportion).toFloat() + Point(x, y = sin(x)).takeUnless { i % 5 == 0 } + } diff --git a/documentation/src/commonMain/kotlin/samples/tutorial/BarChart.kt b/documentation/src/commonMain/kotlin/samples/tutorial/BarChart.kt new file mode 100644 index 000000000..da85d388e --- /dev/null +++ b/documentation/src/commonMain/kotlin/samples/tutorial/BarChart.kt @@ -0,0 +1,52 @@ +package com.juul.krayon.documentation.samples.tutorial + +import com.juul.krayon.color.blue +import com.juul.krayon.element.LineElement +import com.juul.krayon.element.RootElement +import com.juul.krayon.kanvas.HtmlKanvas +import com.juul.krayon.kanvas.Paint +import com.juul.krayon.scale.domain +import com.juul.krayon.scale.range +import com.juul.krayon.scale.scale +import com.juul.krayon.selection.asSelection +import com.juul.krayon.selection.data +import com.juul.krayon.selection.each +import com.juul.krayon.selection.join +import com.juul.krayon.selection.selectAll +import org.w3c.dom.HTMLCanvasElement + +@JsExport +fun setupBarChart(element: HTMLCanvasElement) { + val (width, height) = element.size // 1 + val data = (1..10).toList() // 2 + + val x = scale() // 3 + .domain(0, data.count() - 1) // 3 + .range(0f, width) // 3 + + val y = scale() // 4 + .domain(0, data.max()) // 4 + .range(height, 0f) // 4 + + val barPaint = Paint.Stroke( // 5 + color = blue, // 5 + width = x.scale(1), // 5 + ) + + val root = RootElement() // 6 + root.asSelection() // 7 + .selectAll(LineElement) // 8 + .data(data) // 9 + .join(LineElement) // 10 + .each { (data, index) -> // 11 + startX = x.scale(index) // 12 + startY = y.scale(0) // 12 + endX = x.scale(index) // 12 + endY = y.scale(data) // 12 + paint = barPaint // 13 + } + root.draw(HtmlKanvas(element)) // 14 +} + +private val HTMLCanvasElement.size + get() = width.toFloat() to height.toFloat() diff --git a/documentation/src/commonMain/kotlin/samples/tutorial/Line1.kt b/documentation/src/commonMain/kotlin/samples/tutorial/Line1.kt new file mode 100644 index 000000000..dc4654d30 --- /dev/null +++ b/documentation/src/commonMain/kotlin/samples/tutorial/Line1.kt @@ -0,0 +1,23 @@ +package com.juul.krayon.documentation.samples.tutorial + +import com.juul.krayon.element.LineElement +import com.juul.krayon.element.RootElement +import com.juul.krayon.kanvas.HtmlKanvas +import com.juul.krayon.selection.append +import com.juul.krayon.selection.asSelection +import com.juul.krayon.selection.each +import org.w3c.dom.HTMLCanvasElement + +@JsExport +fun setupLine1(element: HTMLCanvasElement) { + val root = RootElement() // 1 + root.asSelection() // 2 + .append(LineElement) // 3 + .each { + startX = 10f // 4 + startY = 10f // 4 + endX = 90f // 4 + endY = 90f // 4 + } + root.draw(HtmlKanvas(element)) // 5 +} diff --git a/documentation/src/commonMain/kotlin/samples/tutorial/Line2.kt b/documentation/src/commonMain/kotlin/samples/tutorial/Line2.kt new file mode 100644 index 000000000..d0ea0f0a2 --- /dev/null +++ b/documentation/src/commonMain/kotlin/samples/tutorial/Line2.kt @@ -0,0 +1,33 @@ +package com.juul.krayon.documentation.samples.tutorial + +import com.juul.krayon.element.LineElement +import com.juul.krayon.element.RootElement +import com.juul.krayon.kanvas.HtmlKanvas +import com.juul.krayon.selection.asSelection +import com.juul.krayon.selection.data +import com.juul.krayon.selection.each +import com.juul.krayon.selection.join +import com.juul.krayon.selection.selectAll +import org.w3c.dom.HTMLCanvasElement + +private data class Point( + val x: Float, + val y: Float, +) + +@JsExport +fun setupLine2(element: HTMLCanvasElement) { + val root = RootElement() // 1 + root.asSelection() // 2 + .selectAll(LineElement) // 3 + .data(listOf(Point(10f, 10f) to Point(90f, 90f))) // 4 + .join(LineElement) // 5 + .each { (data) -> // 6 + val (start, end) = data // 7 + startX = start.x // 8 + startY = start.y // 8 + endX = end.x // 8 + endY = end.y // 8 + } + root.draw(HtmlKanvas(element)) // 9 +} diff --git a/documentation/src/commonMain/kotlin/samples/updaters/LineChart.kt b/documentation/src/commonMain/kotlin/samples/updaters/LineChart.kt new file mode 100644 index 000000000..bfcfacd8c --- /dev/null +++ b/documentation/src/commonMain/kotlin/samples/updaters/LineChart.kt @@ -0,0 +1,117 @@ +package com.juul.krayon.documentation.samples.updaters + +import androidx.compose.material.Colors +import com.juul.krayon.axis.axisBottom +import com.juul.krayon.axis.axisLeft +import com.juul.krayon.axis.call +import com.juul.krayon.color.darkSlateBlue +import com.juul.krayon.color.steelBlue +import com.juul.krayon.color.white +import com.juul.krayon.compose.toKrayon +import com.juul.krayon.documentation.samples.data.Point +import com.juul.krayon.element.CircleElement +import com.juul.krayon.element.GroupElement +import com.juul.krayon.element.PathElement +import com.juul.krayon.element.RootElement +import com.juul.krayon.element.TransformElement +import com.juul.krayon.element.withKind +import com.juul.krayon.kanvas.Paint +import com.juul.krayon.kanvas.Transform +import com.juul.krayon.scale.domain +import com.juul.krayon.scale.extent +import com.juul.krayon.scale.range +import com.juul.krayon.scale.scale +import com.juul.krayon.selection.append +import com.juul.krayon.selection.asSelection +import com.juul.krayon.selection.data +import com.juul.krayon.selection.each +import com.juul.krayon.selection.join +import com.juul.krayon.selection.selectAll +import com.juul.krayon.shape.line + +private val solidLinePaint = Paint.Stroke(steelBlue, 1f) +private val dashedLinePaint = Paint.Stroke(darkSlateBlue, 0.5f, dash = Paint.Stroke.Dash.Pattern(5f, 5.5f)) +private val circlePaint = Paint.FillAndStroke( + Paint.Fill(white), + Paint.Stroke(steelBlue, 1f), +) + +internal fun lineChart( + root: RootElement, + width: Float, + height: Float, + data: List, + colors: Colors, +) { + val onSurfaceColor = colors.onSurface.toKrayon() + + val leftMargin = 40f + val topMargin = 20f + val rightMargin = 20f + val bottomMargin = 40f + + val innerWidth = width - leftMargin - rightMargin + val innerHeight = height - topMargin - bottomMargin + + val x = scale() + .domain(data.extent { it?.x }) + .range(0f, innerWidth) + val y = scale() + .domain(-1f, 1f) + .range(innerHeight, 0f) + + val line = line() + .x { (p) -> x.scale(p.x) } + .y { (p) -> y.scale(p.y) } + + val body = root.asSelection() + .selectAll(TransformElement.withKind("body")) + .data(listOf(null)) + .join { append(TransformElement).each { kind = "body" } } + .each { + transform = Transform.Translate( + horizontal = leftMargin, + vertical = topMargin, + ) + } + + body.selectAll(TransformElement.withKind("x-axis")) + .data(listOf(null)) + .join { append(TransformElement).each { kind = "x-axis" } } + .each { transform = Transform.Translate(vertical = innerHeight) } + .call(axisBottom(x).apply { + lineColor = onSurfaceColor + textColor = onSurfaceColor + }) + + body.selectAll(GroupElement.withKind("y-axis")) + .data(listOf(null)) + .join { append(GroupElement).each { kind = "y-axis" } } + .call(axisLeft(y).apply { + lineColor = onSurfaceColor + textColor = onSurfaceColor + }) + + body.selectAll(PathElement.withKind("line")) + .data(listOf(data.filterNotNull(), data)) + .join { + append(PathElement).each { (_, i) -> + kind = "line" + paint = if (i == 0) dashedLinePaint else solidLinePaint + } + }.each { (d) -> + path = line.render(d) + } + + body.selectAll(CircleElement) + .data(data.filterNotNull()) + .join { + append(CircleElement).each { + radius = 3f + paint = circlePaint + } + }.each { (d) -> + centerX = x.scale(d.x) + centerY = y.scale(d.y) + } +} diff --git a/documentation/src/commonMain/kotlin/theme/AppTheme.kt b/documentation/src/commonMain/kotlin/theme/AppTheme.kt new file mode 100644 index 000000000..399b0bc69 --- /dev/null +++ b/documentation/src/commonMain/kotlin/theme/AppTheme.kt @@ -0,0 +1,53 @@ +package com.juul.krayon.documentation.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.Colors +import androidx.compose.material.MaterialTheme +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import com.juul.krayon.documentation.generated.resources.Res +import com.juul.krayon.documentation.generated.resources.roboto_mono_regular +import dev.snipme.highlights.model.SyntaxTheme +import dev.snipme.highlights.model.SyntaxThemes +import org.jetbrains.compose.resources.Font + +@Composable +fun AppTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit, +) { + MaterialTheme( + colors = if (darkTheme) darkColors() else lightColors(), + ) { + val robotoMonoFont = Font(Res.font.roboto_mono_regular) + CompositionLocalProvider( + LocalTypography provides Typography(TextStyle(fontFamily = FontFamily(robotoMonoFont))), + LocalSyntaxTheme provides SyntaxThemes.monokai(darkTheme), + ) { + content() + } + } +} + +object AppTheme { + + val colors: Colors + @Composable + @ReadOnlyComposable + get() = MaterialTheme.colors + + val typography: Typography + @Composable + @ReadOnlyComposable + get() = LocalTypography.current + + val highlight: SyntaxTheme + @Composable + @ReadOnlyComposable + get() = LocalSyntaxTheme.current +} diff --git a/documentation/src/commonMain/kotlin/theme/LocalSyntaxTheme.kt b/documentation/src/commonMain/kotlin/theme/LocalSyntaxTheme.kt new file mode 100644 index 000000000..4744265a1 --- /dev/null +++ b/documentation/src/commonMain/kotlin/theme/LocalSyntaxTheme.kt @@ -0,0 +1,9 @@ +package com.juul.krayon.documentation.theme + +import androidx.compose.runtime.ProvidableCompositionLocal +import androidx.compose.runtime.staticCompositionLocalOf +import dev.snipme.highlights.model.SyntaxTheme +import dev.snipme.highlights.model.SyntaxThemes + +val LocalSyntaxTheme: ProvidableCompositionLocal = + staticCompositionLocalOf { SyntaxThemes.default() } diff --git a/documentation/src/commonMain/kotlin/theme/LocalTypography.kt b/documentation/src/commonMain/kotlin/theme/LocalTypography.kt new file mode 100644 index 000000000..9b2c4ee70 --- /dev/null +++ b/documentation/src/commonMain/kotlin/theme/LocalTypography.kt @@ -0,0 +1,7 @@ +package com.juul.krayon.documentation.theme + +import androidx.compose.runtime.ProvidableCompositionLocal +import androidx.compose.runtime.staticCompositionLocalOf + +val LocalTypography: ProvidableCompositionLocal = + staticCompositionLocalOf { Typography.Default } diff --git a/documentation/src/commonMain/kotlin/theme/Typography.kt b/documentation/src/commonMain/kotlin/theme/Typography.kt new file mode 100644 index 000000000..00babac16 --- /dev/null +++ b/documentation/src/commonMain/kotlin/theme/Typography.kt @@ -0,0 +1,16 @@ +package com.juul.krayon.documentation.theme + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.text.TextStyle + +@Immutable +data class Typography( + val code: TextStyle, +) { + + companion object { + val Default = Typography( + code = TextStyle.Default, + ) + } +} diff --git a/documentation/src/jsMain/kotlin/Main.js.kt b/documentation/src/jsMain/kotlin/Main.js.kt new file mode 100644 index 000000000..70c8604cb --- /dev/null +++ b/documentation/src/jsMain/kotlin/Main.js.kt @@ -0,0 +1,22 @@ +package com.juul.krayon.documentation + +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.window.CanvasBasedWindow +import com.juul.krayon.documentation.generated.resources.Res +import com.juul.krayon.documentation.generated.resources.app_title +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.promise +import org.jetbrains.compose.resources.getString +import org.jetbrains.skiko.wasm.onWasmReady + +@OptIn(ExperimentalComposeUiApi::class, DelicateCoroutinesApi::class) +fun main() { + GlobalScope + .promise { getString(Res.string.app_title) } + .then { title -> + onWasmReady { + CanvasBasedWindow(title) { App() } + } + } +} diff --git a/documentation/src/jsMain/resources/index.html b/documentation/src/jsMain/resources/index.html new file mode 100644 index 000000000..96a0f1587 --- /dev/null +++ b/documentation/src/jsMain/resources/index.html @@ -0,0 +1,13 @@ + + + + + + Krayon Documentation + + + + + + + diff --git a/documentation/src/jvmMain/kotlin/Main.jvm.kt b/documentation/src/jvmMain/kotlin/Main.jvm.kt new file mode 100644 index 000000000..d9059ec81 --- /dev/null +++ b/documentation/src/jvmMain/kotlin/Main.jvm.kt @@ -0,0 +1,12 @@ +package com.juul.krayon.documentation + +import androidx.compose.ui.window.singleWindowApplication +import com.juul.krayon.documentation.generated.resources.Res +import com.juul.krayon.documentation.generated.resources.app_title +import kotlinx.coroutines.runBlocking +import org.jetbrains.compose.resources.getString + +fun main() { + val title = runBlocking { getString(Res.string.app_title) } + singleWindowApplication(title = title) { App() } +} diff --git a/documentation/src/macosMain/kotlin/Main.macos.kt b/documentation/src/macosMain/kotlin/Main.macos.kt new file mode 100644 index 000000000..39000a25f --- /dev/null +++ b/documentation/src/macosMain/kotlin/Main.macos.kt @@ -0,0 +1,28 @@ +package com.juul.krayon.documentation + +import androidx.compose.ui.window.Window +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.alloc +import kotlinx.cinterop.nativeHeap +import kotlinx.cinterop.ptr +import platform.AppKit.NSApplication +import platform.ApplicationServices.TransformProcessType +import platform.ApplicationServices.kCurrentProcess +import platform.ApplicationServices.kProcessTransformToForegroundApplication +import platform.darwin.ProcessSerialNumber + +@OptIn(ExperimentalForeignApi::class) +fun main() { + // > There is no support a packaging resources for Compose macOS target. + // https://youtrack.jetbrains.com/issue/CMP-4813#focus=Comments-27-10103096.0-0 +// val title = runBlocking { getString(Res.string.app_title) } + val title = "Krayon: Documentation" + + Window(title) { App() } + val process = nativeHeap.alloc() + process.lowLongOfPSN = kCurrentProcess + TransformProcessType(process.ptr, kProcessTransformToForegroundApplication) + nativeHeap.free(process.rawPtr) + NSApplication.sharedApplication.activate() + NSApplication.sharedApplication.run() +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ab57312c8..66169ad95 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,8 +6,9 @@ android-target = "32" compose = "1.7.3" coroutines = "1.10.1" jacoco = "0.8.7" -jvm-toolchain = "11" +jvm-toolchain = "17" kotlin = "2.1.20" +serialization = "1.8.0" [libraries] androidx-activity-compose = { module = "androidx.activity:activity-compose", version = "1.10.1" } @@ -17,10 +18,15 @@ androidx-test = { module = "androidx.test:core", version = "1.6.1" } coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" } coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } coroutines-js = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core-js", version.ref = "coroutines" } +coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "coroutines" } coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version = "0.6.2" } +highlights = { module = "dev.snipme:highlights", version = "1.0.0" } +markdown = { module = "com.mikepenz:multiplatform-markdown-renderer-m2", version = "0.32.0" } material = { module = "com.google.android.material:material", version = "1.12.0" } +navigation = { module = "org.jetbrains.androidx.navigation:navigation-compose", version = "2.8.0-alpha10" } # Warning: >2.8.0-alpha10 results in: java.lang.UnsatisfiedLinkError: 'long org.jetbrains.skiko.node.RenderNodeContextKt.RenderNodeContext_nMake(boolean)' robolectric = { module = "org.robolectric:robolectric", version = "4.14.1" } +serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "serialization" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } @@ -32,3 +38,4 @@ dokka = { id = "org.jetbrains.dokka", version = "2.0.0" } kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } kotlinter = { id = "org.jmailen.kotlinter", version = "5.0.1" } maven-publish = { id = "com.vanniktech.maven.publish", version = "0.31.0" } +serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 1ae885e87..47521ea4b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,6 +21,7 @@ include( "box", "color", "compose", + "documentation", "element", "element-view", "hierarchy",