import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { kotlin("multiplatform") kotlin("plugin.serialization") id("com.android.kotlin.multiplatform.library") id("org.jetbrains.compose") id("org.jetbrains.kotlin.plugin.compose") // Local cache DB (docs/16: "Local DB (KMP) — SQLDelight"). id("app.cash.sqldelight") } val composeVersion = "1.11.1" // material3 and material-icons-extended are published on their own cadence and // lag the core artifacts; use each one's latest stable release. val material3Version = "1.9.0" val materialIconsVersion = "1.7.3" val coroutinesVersion = "1.11.0" val serializationVersion = "1.11.0" val okhttpVersion = "5.5.0" // 2.0.x is the first line built against Compose 1.8+ (1.9.40 was the last // 1.7.x-compatible release). HTML artifact previews (M9): Android uses the // platform WebView, desktop uses KCEF (JCEF/Chromium, downloaded on first use). val webviewVersion = "2.0.3" val kcefVersion = "2025.03.23" // 0.34.0+ moved to Compose 1.8+ (0.33.0 was the last 1.7.x / Kotlin 2.1.x // release). Provides GFM tables, bold/italic/underscore, and the -code module // for language-aware syntax highlighting (Highlights). val markdownVersion = "0.44.0" // Local cache DB (messages/channels/meta; docs/10 §10.7, docs/16). val sqldelightVersion = "2.3.2" // CameraX + ML Kit for in-app QR pairing (docs/20). Android-only. val cameraxVersion = "1.5.1" val mlKitVersion = "16.1.1" // ── App version (generated) ───────────────────────────────────────────── // The repo-root VERSION file is the single source of truth (bump it to cut // a release; CI can still override with -PappVersion). Generate AppVersion.kt // into commonMain so both targets can display it in Settings and send it to // the gateway (X-Iris-App-Version header). // // A real task with the VERSION file as a DECLARED input: on a // configuration-cache hit the script body does not re-run, so only the task // (keyed on the file's content) can regenerate AppVersion.kt after a bump. // The doLast reads everything from the task's own inputs/outputs (which are // configuration-cache serializable) — it must not reference script-scope // vals, because a .kts script lambda captures the script object and the // configuration cache rejects that. val generatedVersionDir = layout.buildDirectory.dir("generated/app-version") val generateAppVersion by tasks.registering { inputs.file(project.file("../../VERSION")) inputs.property("appVersionOverride", (project.findProperty("appVersion") as? String).orEmpty()) val outFile = generatedVersionDir.map { it.file("AppVersion.kt") } outputs.file(outFile) doLast { val override = inputs.properties["appVersionOverride"] as? String ?: "" val versionFile = inputs.files.singleFile val version = override.ifBlank { versionFile.takeIf { it.exists() }?.readText()?.trim() ?: "0.1.0" } val f = outFile.get().asFile f.parentFile?.mkdirs() f.writeText( """ |package iris | |/** Generated from the repo-root VERSION file - do not edit. */ |object AppVersion { | const val VERSION = "$version" |} | """.trimMargin(), ) } } kotlin { android { namespace = "iris.shared" compileSdk = 37 minSdk = 29 compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } withHostTest { } } jvm("desktop") { // Desktop runs on the Java 21 runtime (see gradle.properties // org.gradle.java.home); target 21 so the Markdown renderer 0.44.0 // (Java-21 bytecode) loads. Android keeps its JVM 17 target above. compilerOptions { jvmTarget.set(JvmTarget.JVM_21) } } sourceSets { // AppVersion.kt is generated from the repo-root VERSION file (see // generateAppVersion above) into commonMain so both targets can read it. commonMain { kotlin.srcDir(generatedVersionDir) } // Both targets are JVM-based (androidTarget + jvm("desktop")), so // shared JVM code (File I/O, SHA-256, media cache) lives in jvmMain. val jvmMain by creating { dependsOn(commonMain.get()) } val androidMain by getting { dependsOn(jvmMain) } val desktopMain by getting { dependsOn(jvmMain) } // Host tests shared by the android (withHostTest) and desktop targets // (e.g. the SQLDelight cache DB tests, which need the JDBC driver). val jvmTest by creating { dependsOn(commonTest.get()) } val androidHostTest by getting { dependsOn(jvmTest) } val desktopTest by getting { dependsOn(jvmTest) } commonMain.dependencies { implementation("org.jetbrains.compose.runtime:runtime:$composeVersion") implementation("org.jetbrains.compose.foundation:foundation:$composeVersion") implementation("org.jetbrains.compose.material3:material3:$material3Version") implementation("org.jetbrains.compose.ui:ui:$composeVersion") // Full Material icon set (Icons.Filled.ContentCopy for the code // copy buttons); -core only ships a small subset. implementation("org.jetbrains.compose.material:material-icons-extended:$materialIconsVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion") // OkHttp is a JVM library; both targets here are JVM-based // (androidTarget + jvm("desktop")), so it resolves on both. implementation("com.squareup.okhttp3:okhttp:$okhttpVersion") // Markdown rendering for chat bubbles (M8): core parser/renderer, // Material 3 themed defaults, and the -code module for syntax // highlighting of fenced code blocks (Highlights). implementation("com.mikepenz:multiplatform-markdown-renderer:$markdownVersion") implementation("com.mikepenz:multiplatform-markdown-renderer-m3:$markdownVersion") implementation("com.mikepenz:multiplatform-markdown-renderer-code:$markdownVersion") // M9: HTML artifact previews — WebView composable (platform WebView // on Android, KCEF/JCEF on desktop). implementation("io.github.kevinnzou:compose-webview-multiplatform:$webviewVersion") // Local cache DB runtime (generated code from commonMain/sqldelight). implementation("app.cash.sqldelight:runtime:$sqldelightVersion") } commonTest.dependencies { implementation(kotlin("test")) } jvmTest.dependencies { implementation(kotlin("test")) // JDBC SQLite driver: in-memory DB for the cache tests. implementation("app.cash.sqldelight:sqlite-driver:$sqldelightVersion") } // M9: KCEF (JCEF/Chromium) for desktop HTML artifact previews. The // webview library exposes it transitively, but we reference KCEF // directly (init + progress) so declare it explicitly. desktopMain.dependencies { implementation("dev.datlag:kcef:$kcefVersion") // Local cache DB: JDBC SQLite driver (file in ~/.iris). implementation("app.cash.sqldelight:sqlite-driver:$sqldelightVersion") } // M4: ExoPlayer (Media3) for inline audio/video playback (Android only). androidMain.dependencies { // M7: EncryptedSharedPreferences for the pairing token // (docs/09 §9.7; AndroidSecureStore). implementation("androidx.security:security-crypto:1.1.0") implementation("androidx.media3:media3-exoplayer:1.11.0") implementation("androidx.media3:media3-ui:1.11.0") // SAF picker (rememberLauncherForActivityResult). implementation("androidx.activity:activity-compose:1.13.0") // CameraX + ML Kit for QR pairing (docs/20): the scanner activity // uses the camera2 CameraX backend and ML Kit's barcode model. implementation("androidx.camera:camera-core:$cameraxVersion") implementation("androidx.camera:camera-camera2:$cameraxVersion") implementation("androidx.camera:camera-lifecycle:$cameraxVersion") implementation("androidx.camera:camera-view:$cameraxVersion") implementation("com.google.mlkit:barcode-scanning:$mlKitVersion") // M5: FCM push (inert without a Firebase project / google-services.json; // the ntfy listener is the fallback). The google-services plugin is // applied conditionally in the app module. implementation("com.google.firebase:firebase-messaging:25.1.2") // Local cache DB: Android SQLite driver (app database dir). implementation("app.cash.sqldelight:android-driver:$sqldelightVersion") } } } sqldelight { databases { create("IrisDatabase") { packageName.set("iris.db") } } } // Every Kotlin compile depends on the generated AppVersion.kt being current. tasks.withType().configureEach { dependsOn(generateAppVersion) }