Repository: futuredapp/kmp-futured-template
Pull request #105: Feature/v3 factory generator
Author: login: RudolfHladik
Created: 2025-02-07T11:25:55Z | Merged: 2025-03-12T13:01:48Z

Generate Component factories by @GenerateFactory annotation with KSP

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
	- Introduced automated factory generation for UI components, enabling consistent and modular instantiation.
	- Added new modules to support enhanced factory-based component creation.

- **Refactor**
	- Streamlined project initialization and navigation logic for clearer, more modular behavior.
	- Updated annotation processing by replacing specialized plugins with a unified, generic processor.
	- Simplified action handling in various components by directly implementing their interfaces.

- **Chores**
	- Improved build configurations and error messages to enhance maintainability and debugging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/buildSrc/src/main/kotlin/app/futured/kmptemplate/gradle/ext/KotlinTargetExt.kt b/buildSrc/src/main/kotlin/app/futured/kmptemplate/gradle/ext/KotlinTargetExt.kt
index 2f6c0851..d300cff6 100644
--- a/buildSrc/src/main/kotlin/app/futured/kmptemplate/gradle/ext/KotlinTargetExt.kt
+++ b/buildSrc/src/main/kotlin/app/futured/kmptemplate/gradle/ext/KotlinTargetExt.kt
@@ -1,12 +1,12 @@
 package app.futured.kmptemplate.gradle.ext
 
-import org.jetbrains.kotlin.gradle.dsl.KotlinTargetContainerWithNativeShortcuts
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
 
 /**
  * Configures supported native iOS targets all at once.
  */
-fun KotlinTargetContainerWithNativeShortcuts.iosTargets(
+fun KotlinMultiplatformExtension.iosTargets(
     targets: List<KotlinNativeTarget> = listOf(
         iosX64(),
         iosArm64(),
diff --git a/convention-plugins/src/main/kotlin/annotations-processor.gradle.kts b/convention-plugins/src/main/kotlin/annotations-processor.gradle.kts
new file mode 100644
index 00000000..38214afe
--- /dev/null
+++ b/convention-plugins/src/main/kotlin/annotations-processor.gradle.kts
@@ -0,0 +1,54 @@
+import org.gradle.accessors.dm.LibrariesForLibs
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
+
+// As of now, we cannot use Gradle version catalogs with Precompiled Script plugins: https://github.com/gradle/gradle/issues/15383
+plugins {
+    id("com.google.devtools.ksp")
+}
+
+// Allow configuring which processor to use (or both)
+abstract class AnnotationsExtension {
+    var useKoin: Boolean = false
+    var useComponentFactory: Boolean = false
+}
+
+val extension = extensions.create("annotations", AnnotationsExtension::class)
+
+// Configure KSP after the extension has been configured
+afterEvaluate {
+    if (extension.useKoin) {
+        ksp {
+            // enable compile time check
+            arg("KOIN_CONFIG_CHECK", "false")
+            // disable default module generation
+            arg("KOIN_DEFAULT_MODULE", "false")
+        }
+    }
+
+    // https://github.com/gradle/gradle/issues/15383
+    val libs = the<LibrariesForLibs>()
+
+    // Add the appropriate dependencies based on configuration
+    dependencies {
+        if (extension.useKoin) {
+            add("kspCommonMainMetadata", libs.koin.ksp.compiler)
+        }
+
+        // Enable source generation by KSP to commonMain only
+        if (extension.useComponentFactory) {
+            add("kspCommonMainMetadata", project(":shared:factory-generator:processor"))
+            // DO NOT add bellow dependencies
+            // add("kspAndroid", Deps.Koin.kspCompiler)
+            // add("kspIosX64", Deps.Koin.kspCompiler)
+            // add("kspIosArm64", Deps.Koin.kspCompiler)
+            // add("kspIosSimulatorArm64", Deps.Koin.kspCompiler)
+        }
+    }
+}
+
+// WORKAROUND: ADD this dependsOn("kspCommonMainKotlinMetadata") instead of above dependencies
+tasks.withType<KotlinCompilationTask<*>>().configureEach {
+    if (name != "kspCommonMainKotlinMetadata") {
+        dependsOn("kspCommonMainKotlinMetadata")
+    }
+}
diff --git a/convention-plugins/src/main/kotlin/koin-annotations.gradle.kts b/convention-plugins/src/main/kotlin/koin-annotations.gradle.kts
deleted file mode 100644
index 7eebb9c4..00000000
--- a/convention-plugins/src/main/kotlin/koin-annotations.gradle.kts
+++ /dev/null
@@ -1,34 +0,0 @@
-import org.gradle.kotlin.dsl.dependencies
-import org.gradle.kotlin.dsl.withType
-import org.gradle.accessors.dm.LibrariesForLibs
-
-// As of now, we cannot use Gradle version catalogs with Precompiled Script plugins: https://github.com/gradle/gradle/issues/15383
-plugins {
-    id("com.google.devtools.ksp")
-}
-
-ksp {
-    // enable compile time check
-    arg("KOIN_CONFIG_CHECK","false")
-    // disable default module generation
-    arg("KOIN_DEFAULT_MODULE","false")
-}
-
-// https://github.com/gradle/gradle/issues/15383
-val libs = the<LibrariesForLibs>()
-// Enable source generation by KSP to commonMain only
-dependencies {
-    add("kspCommonMainMetadata", libs.koin.ksp.compiler)
-    // DO NOT add bellow dependencies
-//    add("kspAndroid", Deps.Koin.kspCompiler)
-//    add("kspIosX64", Deps.Koin.kspCompiler)
-//    add("kspIosArm64", Deps.Koin.kspCompiler)
-//    add("kspIosSimulatorArm64", Deps.Koin.kspCompiler)
-}
-
-// WORKAROUND: ADD this dependsOn("kspCommonMainKotlinMetadata") instead of above dependencies
-tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().configureEach {
-    if (name != "kspCommonMainKotlinMetadata") {
-        dependsOn("kspCommonMainKotlinMetadata")
-    }
-}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 6951f9c0..b42431db 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -38,6 +38,7 @@ profileinstaller = "1.3.1"
 dokkaVersion = "1.9.20"
 google-servicesPlugin = "4.4.2"
 google-firebaseAppDistributionPlugin = "5.0.0"
+poet = "2.0.0"
 
 # Android Namespaces
 project-android-namespace = "app.futured.kmptemplate.android"
@@ -92,6 +93,10 @@ decompose = { group = "com.arkivanov.decompose", name = "decompose", version.ref
 decompose-compose-ext = { group = "com.arkivanov.decompose", name = "extensions-compose", version.ref = "decompose" }
 essenty = { group = "com.arkivanov.essenty", name = "lifecycle", version.ref = "essenty" }
 
+# KSP api
+ksp-lib = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
+poet-interop = { module = "com.squareup:kotlinpoet-ksp", version.ref = "poet"}
+
 # Testing
 kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlin" }
 androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junit" }
@@ -163,7 +168,7 @@ firebase-distribution = { id = "com.google.firebase.appdistribution", version.re
 
 # Precompiled script plugins
 conventions-lint = { id = "conventions-lint" }
-koin-annotations-plugin = { id = "koin-annotations" }
+annotations-processor-plugin = { id = "annotations-processor" }
 
 [bundles]
 compose = [
diff --git a/init_template.kts b/init_template.kts
index 702d626d..e80605a2 100755
--- a/init_template.kts
+++ b/init_template.kts
@@ -32,6 +32,8 @@ renameGradleSubproject("shared/network/rest", appPackageName)
 renameGradleSubproject("shared/persistence", appPackageName)
 renameGradleSubproject("shared/platform", appPackageName)
 renameGradleSubproject("shared/resources", appPackageName)
+//"factory-generator/annotation", TODO ?
+//"factory-generator/processor", TODO ?
 
 findAndReplaceInFileTree(
     parent = Path.of("shared/arkitekt-decompose"),
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 0684f126..77a0246d 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -28,6 +28,8 @@ include(":shared:feature")
 include(":shared:persistence")
 include(":shared:platform")
 include(":shared:resources")
+include(":shared:factory-generator:annotation")
+include(":shared:factory-generator:processor")
 
 includeBuild("convention-plugins")
 include(":baselineprofile")
diff --git a/shared/app/build.gradle.kts b/shared/app/build.gradle.kts
index 9f4b8987..4da63dc1 100644
--- a/shared/app/build.gradle.kts
+++ b/shared/app/build.gradle.kts
@@ -12,12 +12,16 @@ plugins {
     id(libs.plugins.com.android.library.get().pluginId)
     id(libs.plugins.kotlin.multiplatform.get().pluginId)
     id(libs.plugins.conventions.lint.get().pluginId)
-    id(libs.plugins.koin.annotations.plugin.get().pluginId)
+    id(libs.plugins.annotations.processor.plugin.get().pluginId)
 
     alias(libs.plugins.skie)
     alias(libs.plugins.moko.resources)
 }
 
+annotations {
+    useKoin = true
+}
+
 kotlin {
     jvmToolchain(ProjectSettings.Kotlin.JvmToolchainVersion)
     applyDefaultHierarchyTemplate()
diff --git a/shared/factory-generator/annotation/build.gradle.kts b/shared/factory-generator/annotation/build.gradle.kts
new file mode 100644
index 00000000..e135b3ff
--- /dev/null
+++ b/shared/factory-generator/annotation/build.gradle.kts
@@ -0,0 +1,22 @@
+import app.futured.kmptemplate.gradle.configuration.ProjectSettings
+import app.futured.kmptemplate.gradle.ext.iosTargets
+
+plugins {
+    id(libs.plugins.kotlin.multiplatform.get().pluginId)
+}
+
+kotlin {
+    jvmToolchain(ProjectSettings.Kotlin.JvmToolchainVersion)
+
+    applyDefaultHierarchyTemplate()
+
+    jvm()
+
+    iosTargets()
+
+    sourceSets {
+        commonMain {
+            dependencies {  }
+        }
+    }
+}
diff --git a/shared/factory-generator/annotation/src/commonMain/kotlin/app/futured/factorygenerator/annotation/GenerateFactory.kt b/shared/factory-generator/annotation/src/commonMain/kotlin/app/futured/factorygenerator/annotation/GenerateFactory.kt
new file mode 100644
index 00000000..05112744
--- /dev/null
+++ b/shared/factory-generator/annotation/src/commonMain/kotlin/app/futured/factorygenerator/annotation/GenerateFactory.kt
@@ -0,0 +1,5 @@
+package app.futured.factorygenerator.annotation
+
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.SOURCE)
+annotation class GenerateFactory
diff --git a/shared/factory-generator/processor/build.gradle.kts b/shared/factory-generator/processor/build.gradle.kts
new file mode 100644
index 00000000..8c62a5c7
--- /dev/null
+++ b/shared/factory-generator/processor/build.gradle.kts
@@ -0,0 +1,33 @@
+import app.futured.kmptemplate.gradle.configuration.ProjectSettings
+import app.futured.kmptemplate.gradle.ext.iosTargets
+
+plugins {
+    id(libs.plugins.kotlin.multiplatform.get().pluginId)
+}
+
+kotlin {
+    jvmToolchain(ProjectSettings.Kotlin.JvmToolchainVersion)
+
+    applyDefaultHierarchyTemplate()
+
+    jvm()
+
+    iosTargets()
+
+    sourceSets {
+        commonMain {
+            dependencies {
+                implementation(projects.shared.factoryGenerator.annotation)
+            }
+        }
+
+        jvmMain {
+            dependencies {
+                implementation(libs.ksp.lib)
+                implementation(libs.poet.interop)
+            }
+            kotlin.srcDir("src/main/kotlin")
+            resources.srcDir("src/main/resources")
+        }
+    }
+}
diff --git a/shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/ComponentFactoryProcessor.kt b/shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/ComponentFactoryProcessor.kt
new file mode 100644
index 00000000..a142d3bb
--- /dev/null
+++ b/shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/ComponentFactoryProcessor.kt
@@ -0,0 +1,29 @@
+package app.futured.factorygenerator.processor
+
+import app.futured.factorygenerator.annotation.GenerateFactory
+import com.google.devtools.ksp.processing.CodeGenerator
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import kotlin.reflect.KClass
+
+class ComponentFactoryProcessor(
+    private val codeGenerator: CodeGenerator,
+) : SymbolProcessor {
+    override fun process(resolver: Resolver): List<KSAnnotated> {
+        val components: Sequence<KSClassDeclaration> = resolver.findAnnotationsForClass(GenerateFactory::class)
+
+        components.forEach { generateComponent(it) }
+
+        return emptyList()
+    }
+
+    private fun generateComponent(component: KSClassDeclaration) =
+        PoetFactoryComponentGenerator.generateFactory(component, codeGenerator)
+
+    private fun Resolver.findAnnotationsForClass(kClass: KClass<*>): Sequence<KSClassDeclaration> =
+        this.getSymbolsWithAnnotation(kClass.qualifiedName.toString())
+            .filterIsInstance<KSClassDeclaration>()
+}
+
diff --git a/shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/ComponentFactoryProcessorProvider.kt b/shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/ComponentFactoryProcessorProvider.kt
new file mode 100644
index 00000000..be5dab53
--- /dev/null
+++ b/shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/ComponentFactoryProcessorProvider.kt
@@ -0,0 +1,10 @@
+package app.futured.factorygenerator.processor
+
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
+import com.google.devtools.ksp.processing.SymbolProcessorProvider
+
+class ComponentFactoryProcessorProvider : SymbolProcessorProvider {
+    override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor =
+        ComponentFactoryProcessor(codeGenerator = environment.codeGenerator)
+}
diff --git a/shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/PoetFactoryComponentGenerator.kt b/shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/PoetFactoryComponentGenerator.kt
new file mode 100644
index 00000000..1eb572fc
--- /dev/null
+++ b/shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/PoetFactoryComponentGenerator.kt
@@ -0,0 +1,175 @@
+package app.futured.factorygenerator.processor
+
+import com.google.devtools.ksp.processing.CodeGenerator
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSValueParameter
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FileSpec
+import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.TypeName
+import com.squareup.kotlinpoet.TypeSpec
+import com.squareup.kotlinpoet.ksp.toTypeName
+import com.squareup.kotlinpoet.ksp.writeTo
+
+/**
+ * `PoetFactoryComponentGenerator` is responsible for generating the factory object for a given component.
+ *
+ * This object utilizes KotlinPoet to generate the necessary code for creating a factory that
+ * uses Koin for dependency injection. It analyzes a component's constructor parameters to
+ * determine which dependencies need to be manually injected and generates a `createComponent` function
+ * that retrieves these dependencies from the Koin container.
+ *
+ * The generated factory object implements the `KoinComponent` interface and provides a
+ * `createComponent` function to instantiate the target component.
+ *
+ * Key functionalities:
+ * 1.  **Generates Factory Objects:** Creates a factory object for each component annotated
+ *     with a specific annotation (implicitly handled by the ksp processor).
+ * 2.  **Handles Dependency Injection:** Uses Koin's `get()` function to retrieve necessary
+ *     dependencies from the Koin container.
+ * 3.  **Supports Constructor Parameters:** Inspects the component's constructor to identify
+ *     parameters requiring manual injection (marked with `@InjectedParam`).
+ * 4.  **Handles Special Types:** Detects and handles special types like `AppComponentContext`,
+ *     `Navigation`, and `Arg` for specific component needs.
+ */
+object PoetFactoryComponentGenerator {
+
+    private const val INJECTED_PARAM_ANNOTATION = "InjectedParam"
+    private const val APP_COMPONENT_CONTEXT_TYPE_NAME = "AppComponentContext"
+    private const val NAVIGATION_TYPE_NAME = "Navigation"
+
+    private object Imports {
+        const val KOIN_COMPONENT_PACKAGE = "org.koin.core.component"
+        const val KOIN_PARAMETER_PACKAGE = "org.koin.core.parameter"
+        const val KOIN_COMPONENT_CLASS_NAME = "KoinComponent"
+        const val KOIN_GET_FUNCTION_NAME = "get"
+        const val KOIN_PARAMETERS_OF_FUNCTION_NAME = "parametersOf"
+    }
+
+    fun generateFactory(
+        factoryComponent: KSClassDeclaration,
+        codeGenerator: CodeGenerator,
+    ) {
+        // Component name without package e.g. FirstComponent
+        val baseName: String = factoryComponent.qualifiedName?.asString()?.substringAfterLast('.')
+            ?: error("Unable to get base name, component qualified name: ${factoryComponent.qualifiedName?.asString()}")
+
+        val factoryComponentPackageName = factoryComponent.packageName.asString()
+        val factoryClassName = ClassName(
+            packageName = factoryComponentPackageName,
+            simpleNames = listOf("${baseName}Factory"),
+        )
+
+        val koinComponentClass = ClassName(
+            packageName = "org.koin.core.component",
+            simpleNames = listOf("KoinComponent"),
+        )
+
+        val componentTypeSpec = createComponentTypeSpec(
+            factoryClassName = factoryClassName,
+            koinComponentClass = koinComponentClass,
+            createComponentFunction = createComponentFunction(
+                baseName,
+                factoryComponentPackageName,
+                factoryComponent
+            ),
+        )
+
+        val fileSpec = createFileSpec(factoryClassName, componentTypeSpec)
+
+        fileSpec.writeTo(codeGenerator, aggregating = true)
+    }
+
+    private fun createComponentTypeSpec(
+        factoryClassName: ClassName,
+        koinComponentClass: ClassName,
+        createComponentFunction: FunSpec,
+    ) = TypeSpec.objectBuilder(factoryClassName)
+        .addModifiers(KModifier.INTERNAL)
+        .addSuperinterface(superinterface = koinComponentClass)
+        .addFunction(createComponentFunction)
+        .build()
+
+    private fun createFileSpec(factoryClassName: ClassName, componentTypeSpec: TypeSpec) =
+        FileSpec.builder(factoryClassName)
+            .addImport(Imports.KOIN_COMPONENT_PACKAGE, Imports.KOIN_COMPONENT_CLASS_NAME)
+            .addImport(Imports.KOIN_COMPONENT_PACKAGE, Imports.KOIN_GET_FUNCTION_NAME)
+            .addImport(Imports.KOIN_PARAMETER_PACKAGE, Imports.KOIN_PARAMETERS_OF_FUNCTION_NAME)
+            .addType(componentTypeSpec)
+            .build()
+
+    private fun createComponentFunction(
+        baseName: String,
+        factoryComponentPackageName: String,
+        factoryComponent: KSClassDeclaration,
+    ): FunSpec {
+        // All constructor parameters that are annotated with @InjectedParam
+        val unInjectedConstructorParams = factoryComponent.primaryConstructor?.parameters
+            ?.filter { it.annotations.any { it.shortName.asString() == INJECTED_PARAM_ANNOTATION } }
+
+        val appComponentContextType = unInjectedConstructorParams
+            ?.findTypeByName(APP_COMPONENT_CONTEXT_TYPE_NAME)
+            ?: error("Unable to find $APP_COMPONENT_CONTEXT_TYPE_NAME in $baseName's constructor")
+        val navigationType = unInjectedConstructorParams
+            .findTypeByName(NAVIGATION_TYPE_NAME)
+        val argsTypes = unInjectedConstructorParams
+            .filter {
+                it.containsTypeName(NAVIGATION_TYPE_NAME).not() && it.containsTypeName(
+                    APP_COMPONENT_CONTEXT_TYPE_NAME
+                ).not()
+            }
+            .map { it.type.toTypeName() }
+
+        val returnType = ClassName(
+            packageName = factoryComponentPackageName,
+            simpleNames = listOf(baseName),
+        )
+        val paramNameAndType = argsTypes.mapIndexed { index, arg ->
+            val paramName =
+                unInjectedConstructorParams.find { it.type.toTypeName() == arg }?.name?.asString()
+                    ?: "param$index"
+            paramName to arg
+        }
+        val paramNames = paramNameAndType.joinToString { it.first }
+
+        val params = when {
+            argsTypes.isNotEmpty() && navigationType != null -> "parameters = { parametersOf(componentContext, navigation, $paramNames) }"
+            argsTypes.isNotEmpty() -> "parameters = { parametersOf(componentContext, $paramNames) }"
+            navigationType != null -> "parameters = { parametersOf(componentContext, navigation) }"
+            else -> "parameters = { parametersOf(componentContext) }"
+        }
+
+        val createComponentFunSpec = FunSpec.builder("createComponent")
+            .addParameter(name = "componentContext", appComponentContextType)
+
+        if (navigationType != null) {
+            createComponentFunSpec.addParameter(name = "navigation", navigationType)
+        }
+
+        if (paramNameAndType.isNotEmpty()) {
+            paramNameAndType.forEach { (name, type) ->
+                createComponentFunSpec.addParameter(
+                    name = name, type
+                )
+            }
+        }
+
+        return createComponentFunSpec
+            .returns(returnType)
+            .addStatement(
+                "return get(\n" +
+                        "qualifier = null,\n" +
+                        "$params,\n" +
+                        ")",
+            )
+            .build()
+    }
+
+    private fun List<KSValueParameter>.findTypeByName(name: String): TypeName? = this
+        .find { it.containsTypeName(name) }
+        ?.type?.toTypeName()
+
+    private fun KSValueParameter.containsTypeName(name: String): Boolean =
+        this.type.toTypeName().toString().contains(name)
+}
diff --git a/shared/factory-generator/processor/src/jvmMain/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/shared/factory-generator/processor/src/jvmMain/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..434e6ef4
--- /dev/null
+++ b/shared/factory-generator/processor/src/jvmMain/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+app.futured.factorygenerator.processor.ComponentFactoryProcessorProvider
\ No newline at end of file
diff --git a/shared/feature/build.gradle.kts b/shared/feature/build.gradle.kts
index b96d9f26..2954541a 100644
--- a/shared/feature/build.gradle.kts
+++ b/shared/feature/build.gradle.kts
@@ -7,13 +7,18 @@ plugins {
     id(libs.plugins.kotlin.multiplatform.get().pluginId)
     id(libs.plugins.kotlin.parcelize.get().pluginId)
     id(libs.plugins.conventions.lint.get().pluginId)
-    id(libs.plugins.koin.annotations.plugin.get().pluginId)
+    id(libs.plugins.annotations.processor.plugin.get().pluginId)
 
     alias(libs.plugins.compose.compiler)
     alias(libs.plugins.kotlin.serialization)
     alias(libs.plugins.dokka)
 }
 
+annotations {
+    useKoin = true
+    useComponentFactory = true
+}
+
 dependencies {
     implementation(platform(libs.androidx.compose.bom))
 }
@@ -47,6 +52,8 @@ kotlin {
                 implementation(projects.shared.persistence)
                 implementation(projects.shared.arkitektDecompose)
                 implementation(projects.shared.resources)
+                implementation(projects.shared.factoryGenerator.annotation)
+
                 implementation(libs.logging.kermit)
                 implementation(libs.skie.annotations)
                 implementation(libs.network.ktor.http)
diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt
index 8193927f..6d6b30ca 100644
--- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt
+++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt
@@ -1,15 +1,12 @@
 package app.futured.kmptemplate.feature.navigation.home
 
 import app.futured.arkitekt.decompose.ext.asStateFlow
+import app.futured.factorygenerator.annotation.GenerateFactory
 import app.futured.kmptemplate.feature.ui.base.AppComponent
 import app.futured.kmptemplate.feature.ui.base.AppComponentContext
-import app.futured.kmptemplate.feature.ui.base.AppComponentFactory
-import app.futured.kmptemplate.feature.ui.firstScreen.FirstComponent
-import app.futured.kmptemplate.feature.ui.firstScreen.FirstScreenNavigation
-import app.futured.kmptemplate.feature.ui.secondScreen.SecondComponent
-import app.futured.kmptemplate.feature.ui.secondScreen.SecondScreenNavigation
-import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdComponent
-import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenNavigation
+import app.futured.kmptemplate.feature.ui.firstScreen.FirstComponentFactory
+import app.futured.kmptemplate.feature.ui.secondScreen.SecondComponentFactory
+import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdComponentFactory
 import com.arkivanov.decompose.Child
 import com.arkivanov.decompose.router.stack.ChildStack
 import com.arkivanov.decompose.router.stack.childStack
@@ -19,6 +16,7 @@ import kotlinx.coroutines.flow.StateFlow
 import org.koin.core.annotation.Factory
 import org.koin.core.annotation.InjectedParam
 
+@GenerateFactory
 @Factory
 internal class HomeNavHostComponent(
     @InjectedParam componentContext: AppComponentContext,
@@ -35,27 +33,9 @@ internal class HomeNavHostComponent(
         handleBackButton = true,
         childFactory = { config, childCtx ->
             when (config) {
-                HomeConfig.First -> HomeChild.First(
-                    AppComponentFactory.createScreenComponent<FirstComponent, FirstScreenNavigation>(
-                        childContext = childCtx,
-                        navigation = homeNavigator,
-                    ),
-                )
-
-                HomeConfig.Second -> HomeChild.Second(
-                    AppComponentFactory.createScreenComponent<SecondComponent, SecondScreenNavigation>(
-                        childContext = childCtx,
-                        navigation = homeNavigator,
-                    ),
-                )
-
-                is HomeConfig.Third -> HomeChild.Third(
-                    AppComponentFactory.createScreenComponent<ThirdComponent, ThirdScreenNavigation>(
-                        childContext = childCtx,
-                        navigation = homeNavigator,
-                        config.args,
-                    ),
-                )
+                HomeConfig.First -> HomeChild.First(FirstComponentFactory.createComponent(childCtx, homeNavigator))
+                HomeConfig.Second -> HomeChild.Second(SecondComponentFactory.createComponent(childCtx, homeNavigator))
+                is HomeConfig.Third -> HomeChild.Third(ThirdComponentFactory.createComponent(childCtx, homeNavigator, config.args))
             }
         },
     ).asStateFlow()
diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt
index 14476ce2..1181f41b 100644
--- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt
+++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt
@@ -1,13 +1,11 @@
 package app.futured.kmptemplate.feature.navigation.profile
 
 import app.futured.arkitekt.decompose.ext.asStateFlow
+import app.futured.factorygenerator.annotation.GenerateFactory
 import app.futured.kmptemplate.feature.ui.base.AppComponent
 import app.futured.kmptemplate.feature.ui.base.AppComponentContext
-import app.futured.kmptemplate.feature.ui.base.AppComponentFactory
-import app.futured.kmptemplate.feature.ui.profileScreen.ProfileComponent
-import app.futured.kmptemplate.feature.ui.profileScreen.ProfileScreenNavigation
-import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdComponent
-import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenNavigation
+import app.futured.kmptemplate.feature.ui.profileScreen.ProfileComponentFactory
+import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdComponentFactory
 import com.arkivanov.decompose.Child
 import com.arkivanov.decompose.router.stack.ChildStack
 import com.arkivanov.decompose.router.stack.childStack
@@ -17,6 +15,7 @@ import kotlinx.coroutines.flow.StateFlow
 import org.koin.core.annotation.Factory
 import org.koin.core.annotation.InjectedParam
 
+@GenerateFactory
 @Factory
 internal class ProfileNavHostComponent(
     @InjectedParam componentContext: AppComponentContext,
@@ -33,20 +32,8 @@ internal class ProfileNavHostComponent(
         handleBackButton = true,
         childFactory = { config, childCtx ->
             when (config) {
-                ProfileConfig.Profile -> ProfileChild.Profile(
-                    AppComponentFactory.createScreenComponent<ProfileComponent, ProfileScreenNavigation>(
-                        childContext = childCtx,
-                        navigation = navigator,
-                    ),
-                )
-
-                is ProfileConfig.Third -> ProfileChild.Third(
-                    AppComponentFactory.createScreenComponent<ThirdComponent, ThirdScreenNavigation>(
-                        childContext = childCtx,
-                        navigation = navigator,
-                        config.args,
-                    ),
-                )
+                ProfileConfig.Profile -> ProfileChild.Profile(ProfileComponentFactory.createComponent(childCtx, navigator))
+                is ProfileConfig.Third -> ProfileChild.Third(ThirdComponentFactory.createComponent(childCtx, navigator, config.args))
             }
         },
     ).asStateFlow()
diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt
index 7da034d4..b94f5220 100644
--- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt
+++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt
@@ -3,13 +3,10 @@ package app.futured.kmptemplate.feature.navigation.root
 import app.futured.arkitekt.decompose.ext.asStateFlow
 import app.futured.kmptemplate.feature.navigation.deepLink.DeepLinkDestination
 import app.futured.kmptemplate.feature.navigation.deepLink.DeepLinkResolver
-import app.futured.kmptemplate.feature.navigation.signedIn.SignedInNavHostComponent
+import app.futured.kmptemplate.feature.navigation.signedIn.SignedInNavHostComponentFactory
 import app.futured.kmptemplate.feature.ui.base.AppComponent
 import app.futured.kmptemplate.feature.ui.base.AppComponentContext
-import app.futured.kmptemplate.feature.ui.base.AppComponentFactory
-import app.futured.kmptemplate.feature.ui.loginScreen.LoginComponent
-import app.futured.kmptemplate.feature.ui.loginScreen.LoginScreen
-import app.futured.kmptemplate.feature.ui.loginScreen.LoginScreenNavigation
+import app.futured.kmptemplate.feature.ui.loginScreen.LoginComponentFactory
 import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenArgs
 import co.touchlab.kermit.Logger
 import com.arkivanov.decompose.router.slot.ChildSlot
@@ -38,19 +35,18 @@ internal class RootNavHostComponent(
         handleBackButton = false,
         childFactory = { config, childCtx ->
             when (config) {
-                RootConfig.Login -> {
-                    val screen: LoginScreen = AppComponentFactory.createScreenComponent<LoginComponent, LoginScreenNavigation>(
-                        childContext = childCtx,
-                        navigation = rootNavigator,
-                    )
-                    RootChild.Login(screen = screen)
-                }
+                RootConfig.Login -> RootChild.Login(
+                    LoginComponentFactory.createComponent(
+                        childCtx,
+                        rootNavigator,
+                    ),
+                )
 
                 is RootConfig.SignedIn -> RootChild.SignedIn(
-                    navHost = AppComponentFactory.createAppComponent<SignedInNavHostComponent>(
-                        childContext = childCtx,
-                        { rootNavigator.slotNavigator.activate(RootConfig.Login) },
-                        config.initialConfig,
+                    navHost = SignedInNavHostComponentFactory.createComponent(
+                        componentContext = childCtx,
+                        navigationToLogin = { rootNavigator.slotNavigator.activate(RootConfig.Login) },
+                        initialConfig = config.initialConfig,
                     ),
                 )
             }
diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt
index e143695e..5548b75f 100644
--- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt
+++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt
@@ -2,11 +2,11 @@ package app.futured.kmptemplate.feature.navigation.signedIn
 
 import app.futured.arkitekt.decompose.ext.asStateFlow
 import app.futured.arkitekt.decompose.ext.switchTab
-import app.futured.kmptemplate.feature.navigation.home.HomeNavHostComponent
-import app.futured.kmptemplate.feature.navigation.profile.ProfileNavHostComponent
+import app.futured.factorygenerator.annotation.GenerateFactory
+import app.futured.kmptemplate.feature.navigation.home.HomeNavHostComponentFactory
+import app.futured.kmptemplate.feature.navigation.profile.ProfileNavHostComponentFactory
 import app.futured.kmptemplate.feature.ui.base.AppComponent
 import app.futured.kmptemplate.feature.ui.base.AppComponentContext
-import app.futured.kmptemplate.feature.ui.base.AppComponentFactory
 import com.arkivanov.decompose.router.stack.ChildStack
 import com.arkivanov.decompose.router.stack.StackNavigation
 import com.arkivanov.decompose.router.stack.childStack
@@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.stateIn
 import org.koin.core.annotation.Factory
 import org.koin.core.annotation.InjectedParam
 
+@GenerateFactory
 @Factory
 internal class SignedInNavHostComponent(
     @InjectedParam componentContext: AppComponentContext,
@@ -36,17 +37,17 @@ internal class SignedInNavHostComponent(
         childFactory = { config, childCtx ->
             when (config) {
                 is SignedInConfig.Home -> SignedInChild.Home(
-                    AppComponentFactory.createAppComponent<HomeNavHostComponent>(
-                        childContext = childCtx,
-                        config.initialStack,
+                    HomeNavHostComponentFactory.createComponent(
+                        componentContext = childCtx,
+                        initialStack = config.initialStack,
                     ),
                 )
 
                 is SignedInConfig.Profile -> SignedInChild.Profile(
-                    navHost = AppComponentFactory.createAppComponent<ProfileNavHostComponent>(
-                        childContext = childCtx,
-                        navigationToLogin,
-                        config.initialStack,
+                    navHost = ProfileNavHostComponentFactory.createComponent(
+                        componentContext = childCtx,
+                        toLogin = navigationToLogin,
+                        initialStack = config.initialStack,
                     ),
                 )
             }
diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt
index 564ae784..2e325d16 100644
--- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt
+++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt
@@ -22,9 +22,9 @@ interface TEMPLATEScreen {
     }
 }
 
-internal data class TEMPLATEScreenNavigation(
-    val pop: () -> Unit,
-) : NavigationActions
+internal interface TEMPLATEScreenNavigation : NavigationActions {
+    fun TEMPLATEComponent.pop()
+}
 
 data object TEMPLATEViewState
 
@@ -32,11 +32,16 @@ data object TEMPLATEViewState
 internal class TEMPLATEComponent(
     @InjectedParam componentContext: AppComponentContext,
     @InjectedParam override val navigation: TEMPLATEScreenNavigation,
-) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(componentContext, TEMPLATEViewState), TEMPLATEScreen {
-
-    override val actions: TEMPLATEScreen.Actions = object : TEMPLATEScreen.Actions {
-        override fun onBack() = navigation.pop()
-    }
-
+) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(
+        componentContext = componentContext,
+        defaultState = TEMPLATEViewState,
+    ),
+    TEMPLATEScreen,
+    TEMPLATEScreenNavigation by navigation,
+    TEMPLATEScreen.Actions {
+
+    override val actions: TEMPLATEScreen.Actions = this
     override val viewState: StateFlow<TEMPLATEViewState> = componentState.asStateFlow()
+
+    override fun onBack() = pop()
 }
diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt
index ba7aeee2..5bd754b6 100644
--- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt
+++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt
@@ -1,5 +1,6 @@
 package app.futured.kmptemplate.feature.ui.firstScreen
 
+import app.futured.factorygenerator.annotation.GenerateFactory
 import app.futured.kmptemplate.feature.domain.CounterUseCase
 import app.futured.kmptemplate.feature.domain.CounterUseCaseArgs
 import app.futured.kmptemplate.feature.domain.SyncDataUseCase
@@ -16,6 +17,7 @@ import org.koin.core.annotation.InjectedParam
 import kotlin.time.Duration.Companion.milliseconds
 
 @Factory
+@GenerateFactory
 internal class FirstComponent(
     @InjectedParam componentContext: AppComponentContext,
     @InjectedParam override val navigation: FirstScreenNavigation,
@@ -26,7 +28,8 @@ internal class FirstComponent(
         defaultState = FirstViewState(),
     ),
     FirstScreen,
-    FirstScreenNavigation by navigation {
+    FirstScreenNavigation by navigation,
+    FirstScreen.Actions {
 
     companion object {
         private const val COUNTER_ALERT = 10L
@@ -35,10 +38,7 @@ internal class FirstComponent(
     private val logger = Logger.withTag("FirstComponent")
 
     override val viewState: StateFlow<FirstViewState> = componentState.asStateFlow()
-
-    override val actions: FirstScreen.Actions = object : FirstScreen.Actions {
-        override fun onNext() = navigateToSecond()
-    }
+    override val actions: FirstScreen.Actions = this
 
     init {
         doOnCreate {
@@ -47,6 +47,8 @@ internal class FirstComponent(
         }
     }
 
+    override fun onNext() = navigateToSecond()
+
     private fun syncData() = syncDataUseCase.execute {
         onError { error ->
             logger.e(error) { error.message.toString() }
diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginComponent.kt
index 6d69979a..db1b45a3 100644
--- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginComponent.kt
+++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginComponent.kt
@@ -1,11 +1,13 @@
 package app.futured.kmptemplate.feature.ui.loginScreen
 
+import app.futured.factorygenerator.annotation.GenerateFactory
 import app.futured.kmptemplate.feature.ui.base.AppComponentContext
 import app.futured.kmptemplate.feature.ui.base.ScreenComponent
 import kotlinx.coroutines.flow.StateFlow
 import org.koin.core.annotation.Factory
 import org.koin.core.annotation.InjectedParam
 
+@GenerateFactory
 @Factory
 internal class LoginComponent(
     @InjectedParam componentContext: AppComponentContext,
@@ -15,11 +17,11 @@ internal class LoginComponent(
         defaultState = LoginViewState,
     ),
     LoginScreen,
-    LoginScreenNavigation by navigation {
-
-    override val actions: LoginScreen.Actions = object : LoginScreen.Actions {
-        override fun onLoginClick() = toSignedIn()
-    }
+    LoginScreenNavigation by navigation,
+    LoginScreen.Actions {
 
+    override val actions: LoginScreen.Actions = this
     override val viewState: StateFlow<LoginViewState> = componentState.asStateFlow()
+
+    override fun onLoginClick() = toSignedIn()
 }
diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/FruitPickerComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/FruitPickerComponent.kt
index 87efd46d..cc64b8bd 100644
--- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/FruitPickerComponent.kt
+++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/FruitPickerComponent.kt
@@ -1,5 +1,6 @@
 package app.futured.kmptemplate.feature.ui.picker
 
+import app.futured.factorygenerator.annotation.GenerateFactory
 import app.futured.kmptemplate.feature.ui.base.AppComponentContext
 import app.futured.kmptemplate.feature.ui.base.ScreenComponent
 import app.futured.kmptemplate.resources.MR
@@ -13,20 +14,17 @@ import org.koin.core.annotation.Factory
 import org.koin.core.annotation.InjectedParam
 import kotlin.time.Duration.Companion.seconds
 
+@GenerateFactory
 @Factory
 internal class FruitPickerComponent(
     @InjectedParam componentContext: AppComponentContext,
     @InjectedParam override val navigation: PickerNavigation,
-) : ScreenComponent<PickerState, Nothing, PickerNavigation>(componentContext, PickerState()),
-    Picker {
-
-    override val actions: Picker.Actions = object : Picker.Actions {
-        override fun onPick(item: String) = navigation.dismiss(item)
-        override fun onDismiss() = navigation.dismiss(null)
-    }
+) : ScreenComponent<PickerState, Nothing, PickerNavigation>(componentContext, PickerState()), Picker, Picker.Actions {
 
     override val viewState: StateFlow<PickerState> = componentState.asStateFlow()
 
+    override val actions: Picker.Actions = this
+
     init {
         doOnCreate {
             launchWithHandler {
@@ -45,4 +43,8 @@ internal class FruitPickerComponent(
             }
         }
     }
+
+    override fun onPick(item: String) = navigation.dismiss(item)
+
+    override fun onDismiss() = navigation.dismiss(null)
 }
diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/VegetablePickerComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/VegetablePickerComponent.kt
index b7cb4638..a4967b7d 100644
--- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/VegetablePickerComponent.kt
+++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/VegetablePickerComponent.kt
@@ -1,5 +1,6 @@
 package app.futured.kmptemplate.feature.ui.picker
 
+import app.futured.factorygenerator.annotation.GenerateFactory
 import app.futured.kmptemplate.feature.ui.base.AppComponentContext
 import app.futured.kmptemplate.feature.ui.base.ScreenComponent
 import app.futured.kmptemplate.resources.MR
@@ -13,18 +14,14 @@ import org.koin.core.annotation.Factory
 import org.koin.core.annotation.InjectedParam
 import kotlin.time.Duration.Companion.seconds
 
+@GenerateFactory
 @Factory
 internal class VegetablePickerComponent(
     @InjectedParam componentContext: AppComponentContext,
     @InjectedParam override val navigation: PickerNavigation,
-) : ScreenComponent<PickerState, Nothing, PickerNavigation>(componentContext, PickerState()),
-    Picker {
-
-    override val actions: Picker.Actions = object : Picker.Actions {
-        override fun onPick(item: String) = navigation.dismiss(item)
-        override fun onDismiss() = navigation.dismiss(null)
-    }
+) : ScreenComponent<PickerState, Nothing, PickerNavigation>(componentContext, PickerState()), Picker, Picker.Actions {
 
+    override val actions: Picker.Actions = this
     override val viewState: StateFlow<PickerState> = componentState.asStateFlow()
 
     init {
@@ -46,4 +43,8 @@ internal class VegetablePickerComponent(
             }
         }
     }
+
+    override fun onPick(item: String) = navigation.dismiss(item)
+
+    override fun onDismiss() = navigation.dismiss(null)
 }
diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileComponent.kt
index 0628db19..9c9203a0 100644
--- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileComponent.kt
+++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileComponent.kt
@@ -1,11 +1,13 @@
 package app.futured.kmptemplate.feature.ui.profileScreen
 
+import app.futured.factorygenerator.annotation.GenerateFactory
 import app.futured.kmptemplate.feature.ui.base.AppComponentContext
 import app.futured.kmptemplate.feature.ui.base.ScreenComponent
 import kotlinx.coroutines.flow.StateFlow
 import org.koin.core.annotation.Factory
 import org.koin.core.annotation.InjectedParam
 
+@GenerateFactory
 @Factory
 internal class ProfileComponent(
     @InjectedParam componentContext: AppComponentContext,
@@ -15,12 +17,11 @@ internal class ProfileComponent(
         ProfileViewState,
     ),
     ProfileScreen,
-    ProfileScreenNavigation by navigation {
-
-    override val actions: ProfileScreen.Actions = object : ProfileScreen.Actions {
-        override fun onLogout() = navigateToLogin()
-        override fun onThird() = navigateToThird("hello third from profile")
-    }
+    ProfileScreenNavigation by navigation,
+    ProfileScreen.Actions {
 
+    override val actions: ProfileScreen.Actions = this
     override val viewState: StateFlow<ProfileViewState> = componentState
+    override fun onLogout() = navigateToLogin()
+    override fun onThird() = navigateToThird("hello third from profile")
 }
diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt
index 42c1c526..0e611d3b 100644
--- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt
+++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt
@@ -1,13 +1,13 @@
 package app.futured.kmptemplate.feature.ui.secondScreen
 
 import app.futured.arkitekt.decompose.ext.asStateFlow
+import app.futured.factorygenerator.annotation.GenerateFactory
 import app.futured.kmptemplate.feature.ui.base.AppComponentContext
-import app.futured.kmptemplate.feature.ui.base.AppComponentFactory
 import app.futured.kmptemplate.feature.ui.base.ScreenComponent
-import app.futured.kmptemplate.feature.ui.picker.FruitPickerComponent
+import app.futured.kmptemplate.feature.ui.picker.FruitPickerComponentFactory
 import app.futured.kmptemplate.feature.ui.picker.Picker
 import app.futured.kmptemplate.feature.ui.picker.PickerNavigation
-import app.futured.kmptemplate.feature.ui.picker.VegetablePickerComponent
+import app.futured.kmptemplate.feature.ui.picker.VegetablePickerComponentFactory
 import com.arkivanov.decompose.router.slot.ChildSlot
 import com.arkivanov.decompose.router.slot.SlotNavigation
 import com.arkivanov.decompose.router.slot.activate
@@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.StateFlow
 import org.koin.core.annotation.Factory
 import org.koin.core.annotation.InjectedParam
 
+@GenerateFactory
 @Factory
 internal class SecondComponent(
     @InjectedParam componentContext: AppComponentContext,
@@ -26,9 +27,11 @@ internal class SecondComponent(
         defaultState = SecondViewState,
     ),
     SecondScreen,
-    SecondScreenNavigation by navigation {
+    SecondScreenNavigation by navigation,
+    SecondScreen.Actions {
 
     override val viewState: StateFlow<SecondViewState> = componentState
+    override val actions: SecondScreen.Actions = this
 
     private val pickerNavigator = SlotNavigation<SecondScreen.PickerType>()
     private val pickerNavigation = PickerNavigation(
@@ -45,23 +48,24 @@ internal class SecondComponent(
         serializer = SecondScreen.PickerType.serializer(),
         childFactory = { type, childContext ->
             when (type) {
-                SecondScreen.PickerType.Fruit -> AppComponentFactory.createAppComponent<FruitPickerComponent>(
-                    childContext,
-                    pickerNavigation,
+                SecondScreen.PickerType.Fruit -> FruitPickerComponentFactory.createComponent(
+                    componentContext = childContext,
+                    navigation = pickerNavigation,
                 )
 
-                SecondScreen.PickerType.Vegetable -> AppComponentFactory.createAppComponent<VegetablePickerComponent>(
-                    childContext,
-                    pickerNavigation,
+                SecondScreen.PickerType.Vegetable -> VegetablePickerComponentFactory.createComponent(
+                    componentContext = childContext,
+                    navigation = pickerNavigation,
                 )
             }
         },
     ).asStateFlow()
 
-    override val actions: SecondScreen.Actions = object : SecondScreen.Actions {
-        override fun onBack() = pop()
-        override fun onPickVeggie() = pickerNavigator.activate(SecondScreen.PickerType.Vegetable)
-        override fun onPickFruit() = pickerNavigator.activate(SecondScreen.PickerType.Fruit)
-        override fun onPickerDismissed() = pickerNavigator.dismiss()
-    }
+    override fun onBack() = pop()
+
+    override fun onPickVeggie() = pickerNavigator.activate(SecondScreen.PickerType.Vegetable)
+
+    override fun onPickFruit() = pickerNavigator.activate(SecondScreen.PickerType.Fruit)
+
+    override fun onPickerDismissed() = pickerNavigator.dismiss()
 }
diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt
index c69f46ba..e33abf24 100644
--- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt
+++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt
@@ -1,5 +1,6 @@
 package app.futured.kmptemplate.feature.ui.thirdScreen
 
+import app.futured.factorygenerator.annotation.GenerateFactory
 import app.futured.kmptemplate.feature.ui.base.AppComponentContext
 import app.futured.kmptemplate.feature.ui.base.ScreenComponent
 import app.futured.kmptemplate.resources.MR
@@ -8,6 +9,7 @@ import kotlinx.coroutines.flow.StateFlow
 import org.koin.core.annotation.Factory
 import org.koin.core.annotation.InjectedParam
 
+@GenerateFactory
 @Factory
 internal class ThirdComponent(
     @InjectedParam componentContext: AppComponentContext,
@@ -18,11 +20,12 @@ internal class ThirdComponent(
         defaultState = ThirdViewState(text = MR.strings.third_screen_text.format(args.id)),
     ),
     ThirdScreen,
-    ThirdScreenNavigation by navigation {
+    ThirdScreenNavigation by navigation,
+    ThirdScreen.Actions {
 
     override val viewState: StateFlow<ThirdViewState> = componentState
 
-    override val actions: ThirdScreen.Actions = object : ThirdScreen.Actions {
-        override fun onBack() = pop()
-    }
+    override val actions: ThirdScreen.Actions = this
+
+    override fun onBack() = pop()
 }
diff --git a/shared/network/graphql/build.gradle.kts b/shared/network/graphql/build.gradle.kts
index 0f00cda7..8c5ff062 100644
--- a/shared/network/graphql/build.gradle.kts
+++ b/shared/network/graphql/build.gradle.kts
@@ -7,12 +7,16 @@ plugins {
     id(libs.plugins.com.android.library.get().pluginId)
     id(libs.plugins.kotlin.multiplatform.get().pluginId)
     id(libs.plugins.conventions.lint.get().pluginId)
-    id(libs.plugins.koin.annotations.plugin.get().pluginId)
+    id(libs.plugins.annotations.processor.plugin.get().pluginId)
 
     alias(libs.plugins.apollo)
     alias(libs.plugins.buildkonfig)
 }
 
+annotations {
+    useKoin = true
+}
+
 kotlin {
     jvmToolchain(ProjectSettings.Kotlin.JvmToolchainVersion)
 
diff --git a/shared/network/rest/build.gradle.kts b/shared/network/rest/build.gradle.kts
index b5cb86fa..25f21942 100644
--- a/shared/network/rest/build.gradle.kts
+++ b/shared/network/rest/build.gradle.kts
@@ -7,13 +7,17 @@ plugins {
     id(libs.plugins.com.android.library.get().pluginId)
     id(libs.plugins.kotlin.multiplatform.get().pluginId)
     id(libs.plugins.conventions.lint.get().pluginId)
-    id(libs.plugins.koin.annotations.plugin.get().pluginId)
+    id(libs.plugins.annotations.processor.plugin.get().pluginId)
 
     alias(libs.plugins.ktorfit)
     alias(libs.plugins.kotlin.serialization)
     alias(libs.plugins.buildkonfig)
 }
 
+annotations {
+    useKoin = true
+}
+
 kotlin {
     jvmToolchain(ProjectSettings.Kotlin.JvmToolchainVersion)
 


Reviews:
user: login: matejsemancik
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-14T15:09:55Z
user: login: coderabbitai[bot]
body: > [!CAUTION]
> Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.  If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

**Actionable comments posted: 1**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt (1)</summary><blockquote>

`44-45`: _⚠️ Potential issue_

**Remove duplicate/leftover code**

These lines appear to be leftover from a previous implementation or a copy-paste error. They define an `onBack()` method outside of any class or interface declaration and should be removed.


```diff
-    override fun onBack() = pop()
-}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (10)</summary><blockquote>

<details>
<summary>convention-plugins/src/main/kotlin/annotations-processor.gradle.kts (1)</summary><blockquote>

`40-44`: **Consider adding explanation for DO NOT comments**

The comment warns against adding certain dependencies, but doesn't explain why. Including a brief explanation of the reason would help future maintainers understand the constraints.

```diff
- // DO NOT add bellow dependencies
+ // DO NOT add below dependencies - they would cause multiple symbol processing runs
+ // and potentially conflicting generated code. Use only kspCommonMainMetadata.
```

</blockquote></details>
<details>
<summary>shared/factory-generator/processor/build.gradle.kts (1)</summary><blockquote>

`1-34`: **Build configuration looks good for the annotation processor**

The Gradle configuration for the factory generator processor is well-structured for a Kotlin Multiplatform project. The dependencies on KSP and Poet libraries are appropriate for an annotation processor that generates code.


Consider adding a test source set to implement unit tests for the annotation processor. Testing annotation processors is important to ensure they handle different edge cases correctly, such as:
- Classes with different visibility modifiers
- Classes with complex constructor signatures
- Error handling for invalid annotation usage

```diff
 sourceSets {
     commonMain {
         dependencies {
             implementation(projects.shared.factoryGenerator.annotation)
         }
     }

     jvmMain {
         dependencies {
             implementation(libs.ksp.lib)
             implementation(libs.poet.interop)
         }
         kotlin.srcDir("src/main/kotlin")
         resources.srcDir("src/main/resources")
     }
+    
+    jvmTest {
+        dependencies {
+            implementation(kotlin("test"))
+            implementation(libs.ksp.lib)
+            // Add testing libraries for annotation processors
+            implementation(libs.compile.testing)
+        }
+    }
 }
```

</blockquote></details>
<details>
<summary>shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/ComponentFactoryProcessor.kt (1)</summary><blockquote>

`22-23`: **Validate or handle potential code generation conflicts.**

`PoetFactoryComponentGenerator.generateFactory` is invoked directly without checks for name collisions or existing files. Consider adding a safeguard or logging in case a generated file already exists, to avoid unexpected overwrites when multiple classes share the same generated filename.

</blockquote></details>
<details>
<summary>shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt (2)</summary><blockquote>

`28-28`: **Clarify component interface implementations.**

You've combined `SecondScreen`, `SecondScreenNavigation`, and `SecondScreen.Actions` into one class. This is concise, but make sure it doesn't overcomplicate the component's responsibilities. For larger features, separating concerns might keep the code more maintainable.

---

`61-67`: **Review navigation calls for potential error handling.**

The overridden methods (`onBack`, `onPickVeggie`, etc.) rely on `pickerNavigator` and `pop()`. Confirm these calls are safe across all lifecycles (e.g., if navigation is missing or the router is already inactive). If there's a chance these could fail, guard the calls or add explicit error handling.

</blockquote></details>
<details>
<summary>init_template.kts (4)</summary><blockquote>

`20-22`: **Prompt user for framework name earlier if needed.**

You're reading user input for project name, package, and framework only after defining constants. Consider prompting the user for inputs upfront, so constants referencing them are declared immediately after.

---

`177-200`: **Consider supporting additional source sets.**

`renameGradleSubproject` enumerates several standard source sets (main, commonMain, iosMain, etc.). If you add watchOS, tvOS, or other shared native targets in the future, remember to add them here for consistent renaming.

---

`231-245`: **Handle partial moves or existing file collisions.**

`moveFileTree` might overwrite files if collisions occur. Ensure you either have checks in place or handle them gracefully with logs or warnings to avoid accidental overwrites.

---

`287-289`: **Optional function usage.**

`removeLicense()` is only used once before deleting the file in line 170. If it’s not reused, consider removing its definition to keep your script minimal.

</blockquote></details>
<details>
<summary>shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/PoetFactoryComponentGenerator.kt (1)</summary><blockquote>

`97-140`: **Parameter injection logic is flexible and well-structured.**  
This function handles `AppComponentContext`, `Navigation`, and optional `Arg` parameters by filtering constructor parameters with the `@InjectedParam` annotation. The approach to building the final `createComponent` function is clean and easily extended to new types if needed.  

Consider also validating that no unexpected duplicates are passed. For instance, if multiple `@InjectedParam` fields of the same type are present, it might cause confusion or runtime errors.

</blockquote></details>

</blockquote></details>

<details>
<summary>🛑 Comments failed to post (1)</summary><blockquote>

<details>
<summary>init_template.kts (1)</summary><blockquote>

25-37: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Validate new subproject rename logic.**

The repeated calls to `renameGradleSubproject` help rename multiple subprojects. Ensure all expected modules (like `factory-generator/annotation`, `factory-generator/processor`) are included if they need renaming. Otherwise, remove commented-out lines if they’re no longer relevant.  


You can run a quick search command to verify if subprojects for `factory-generator` exist:

---

🏁 Script executed:

```shell
#!/bin/bash
fd -I -t d factory-generator
```

Length of output: 56

---



</details>

**Factory-Generator Rename Clarification**

I verified that the only directory matching “factory-generator” is `shared/factory-generator` (no separate `annotation` or `processor` subdirectories exist). Please either add the corresponding `renameGradleSubproject` calls if those submodules are expected to be added later, or remove the commented-out lines to clean up the code.

</blockquote></details>

</blockquote></details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-11T16:15:05Z
user: login: xmadera
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-11T16:17:16Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-11T16:17:59Z
user: login: github-actions[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-12T10:27:40Z
user: login: github-actions[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-12T10:27:41Z
user: login: github-actions[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-12T10:27:41Z
user: login: github-actions[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-12T10:27:42Z
user: login: github-actions[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-12T11:56:18Z
user: login: github-actions[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-12T11:56:19Z
user: login: matejsemancik
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-12T13:01:39Z
user: login: matejsemancik
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-14T07:27:34Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-14T07:28:34Z

Review comments:
path: convention-plugins/src/main/kotlin/component-factory-annotations.gradle.kts
user: login: matejsemancik
body: This convention plugin is basically the same as `koin-annotations` plugin we already have. From what I see, this plugin just configures KSP in various project submodules. Maybe we can merge them together under single plugin?
created_at: 2025-02-14T14:39:23Z
author_association: MEMBER
diff_hunk: @@ -32,6 +32,8 @@ renameGradleSubproject("shared/network/rest", appPackageName)
 renameGradleSubproject("shared/persistence", appPackageName)
 renameGradleSubproject("shared/platform", appPackageName)
 renameGradleSubproject("shared/resources", appPackageName)
+//"factory-generator/annotation", TODO ?
+//"factory-generator/processor", TODO ?
path: init_template.kts
user: login: matejsemancik
body: Are these TODOs still valid? If yes, can we fix them?
created_at: 2025-02-14T14:40:32Z
author_association: MEMBER
diff_hunk: @@ -28,6 +28,8 @@ include(":shared:feature")
 include(":shared:persistence")
 include(":shared:platform")
 include(":shared:resources")
+include(":shared:factory-generator:annotation")
+include(":shared:factory-generator:processor")
path: settings.gradle.kts
user: login: matejsemancik
body: Naming proposal
- `shared:arkitekt-decompose:annotation`
- `shared:arkitekt-decompose:compiler`
created_at: 2025-02-14T14:41:36Z
author_association: MEMBER
diff_hunk: @@ -22,21 +22,21 @@ interface TEMPLATEScreen {
     }
 }
 
-internal data class TEMPLATEScreenNavigation(
-    val pop: () -> Unit,
-) : NavigationActions
+internal interface TEMPLATEScreenNavigation : NavigationActions {
+    fun pop()
path: shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt
user: login: matejsemancik
body: Should't be like this?

```suggestion
    fun TEMPLATEComponent.pop()
```
created_at: 2025-02-14T14:55:09Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,175 @@
+package app.futured.factorygenerator.processor
+
+import com.google.devtools.ksp.processing.CodeGenerator
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSValueParameter
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FileSpec
+import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.TypeName
+import com.squareup.kotlinpoet.TypeSpec
+import com.squareup.kotlinpoet.ksp.toTypeName
+import com.squareup.kotlinpoet.ksp.writeTo
+
+/**
+ * `PoetFactoryComponentGenerator` is responsible for generating the factory object for a given component.
+ *
+ * This object utilizes KotlinPoet to generate the necessary code for creating a factory that
+ * uses Koin for dependency injection. It analyzes a component's constructor parameters to
+ * determine which dependencies need to be manually injected and generates a `createComponent` function
+ * that retrieves these dependencies from the Koin container.
+ *
+ * The generated factory object implements the `KoinComponent` interface and provides a
+ * `createComponent` function to instantiate the target component.
+ *
+ * Key functionalities:
+ * 1.  **Generates Factory Objects:** Creates a factory object for each component annotated
+ *     with a specific annotation (implicitly handled by the ksp processor).
+ * 2.  **Handles Dependency Injection:** Uses Koin's `get()` function to retrieve necessary
+ *     dependencies from the Koin container.
+ * 3.  **Supports Constructor Parameters:** Inspects the component's constructor to identify
+ *     parameters requiring manual injection (marked with `@InjectedParam`).
+ * 4.  **Handles Special Types:** Detects and handles special types like `AppComponentContext`,
+ *     `Navigation`, and `Arg` for specific component needs.
+ */
+object PoetFactoryComponentGenerator {
+
+    private const val INJECTED_PARAM_ANNOTATION = "InjectedParam"
+    private const val APP_COMPONENT_CONTEXT_TYPE_NAME = "AppComponentContext"
+    private const val NAVIGATION_TYPE_NAME = "Navigation"
+
+    private object Imports {
+        const val KOIN_COMPONENT_PACKAGE = "org.koin.core.component"
+        const val KOIN_PARAMETER_PACKAGE = "org.koin.core.parameter"
+        const val KOIN_COMPONENT_CLASS_NAME = "KoinComponent"
+        const val KOIN_GET_FUNCTION_NAME = "get"
+        const val KOIN_PARAMETERS_OF_FUNCTION_NAME = "parametersOf"
+    }
+
+    fun generateFactory(
+        factoryComponent: KSClassDeclaration,
+        codeGenerator: CodeGenerator,
+    ) {
+        // Component name without package e.g. FirstComponent
+        val baseName: String = factoryComponent.qualifiedName?.asString()?.substringAfterLast('.')
+            ?: error("Unable to get base name")
path: shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/PoetFactoryComponentGenerator.kt
user: login: matejsemancik
body: If something goes wrong, I, as a user, would like to know why base name could not be generated

```suggestion
            ?: error("Unable to get base name, component qualified name: ${factoryComponent.qualifiedName?.asString()}")
```
created_at: 2025-02-14T15:07:31Z
author_association: MEMBER
diff_hunk: @@ -28,6 +28,8 @@ include(":shared:feature")
 include(":shared:persistence")
 include(":shared:platform")
 include(":shared:resources")
+include(":shared:factory-generator:annotation")
+include(":shared:factory-generator:processor")
path: settings.gradle.kts
user: login: xmadera
body: Not 100 percent sure with this naming, especially changing the processor to compiler, I think processor is the better terminology for this case, I also feel the separation in factory-generator is cleaner, but I'm just nitpicking

What do you think @coderabbitai ?
created_at: 2025-03-11T16:17:16Z
author_association: MEMBER
diff_hunk: @@ -28,6 +28,8 @@ include(":shared:feature")
 include(":shared:persistence")
 include(":shared:platform")
 include(":shared:resources")
+include(":shared:factory-generator:annotation")
+include(":shared:factory-generator:processor")
path: settings.gradle.kts
user: login: coderabbitai[bot]
body: @xmadera I agree with your assessment. In the Kotlin Symbol Processing (KSP) ecosystem, "processor" is indeed the more accurate terminology - it's used throughout the official KSP documentation and aligns with class names like `SymbolProcessor` and `SymbolProcessorProvider`.

Regarding the module structure, the separation as `:shared:factory-generator:annotation` and `:shared:factory-generator:processor` makes more sense if this factory generation capability is designed to be a standalone feature that could potentially be used beyond just the decompose module. This separation clearly communicates its purpose and maintains a clean architectural boundary.

If these modules were placed under `:shared:arkitekt-decompose`, it would imply they're specifically tied to and only usable with the decompose functionality, which might not be the case here.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-11T16:17:59Z
author_association: NONE
diff_hunk: @@ -22,21 +22,24 @@ interface TEMPLATEScreen {
     }
 }
 
-internal data class TEMPLATEScreenNavigation(
-    val pop: () -> Unit,
-) : NavigationActions
+internal interface TEMPLATEScreenNavigation : NavigationActions {
+    fun TEMPLATEComponent.pop()
+}
 
 data object TEMPLATEViewState
 
 @Factory
 internal class TEMPLATEComponent(
     @InjectedParam componentContext: AppComponentContext,
     @InjectedParam override val navigation: TEMPLATEScreenNavigation,
-) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(componentContext, TEMPLATEViewState), TEMPLATEScreen {
-
-    override val actions: TEMPLATEScreen.Actions = object : TEMPLATEScreen.Actions {
-        override fun onBack() = navigation.pop()
-    }
+) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(
+    componentContext = componentContext,
path: shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt
user: login: github-actions[bot]
body: <table data-meta="generated_by_danger-pr">
  <tbody>
    <tr>
      <td>:warning:</td>
      <td width="100%" data-sticky="false"><span data-href="https://github.com/futuredapp/kmp-futured-template/blob/54f944bf2d0514e0cf8f31d73b26b7ff4bcd4070/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt#L36"></span>Unexpected indentation (4) (should be 8)</td>
    </tr>
  </tbody>
</table>




created_at: 2025-03-12T10:27:40Z
author_association: NONE
diff_hunk: @@ -22,21 +22,24 @@ interface TEMPLATEScreen {
     }
 }
 
-internal data class TEMPLATEScreenNavigation(
-    val pop: () -> Unit,
-) : NavigationActions
+internal interface TEMPLATEScreenNavigation : NavigationActions {
+    fun TEMPLATEComponent.pop()
+}
 
 data object TEMPLATEViewState
 
 @Factory
 internal class TEMPLATEComponent(
     @InjectedParam componentContext: AppComponentContext,
     @InjectedParam override val navigation: TEMPLATEScreenNavigation,
-) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(componentContext, TEMPLATEViewState), TEMPLATEScreen {
-
-    override val actions: TEMPLATEScreen.Actions = object : TEMPLATEScreen.Actions {
-        override fun onBack() = navigation.pop()
-    }
+) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(
+    componentContext = componentContext,
+    defaultState = TEMPLATEViewState,
path: shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt
user: login: github-actions[bot]
body: <table data-meta="generated_by_danger-pr">
  <tbody>
    <tr>
      <td>:warning:</td>
      <td width="100%" data-sticky="false"><span data-href="https://github.com/futuredapp/kmp-futured-template/blob/54f944bf2d0514e0cf8f31d73b26b7ff4bcd4070/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt#L37"></span>Unexpected indentation (4) (should be 8)</td>
    </tr>
  </tbody>
</table>




created_at: 2025-03-12T10:27:41Z
author_association: NONE
diff_hunk: @@ -22,21 +22,24 @@ interface TEMPLATEScreen {
     }
 }
 
-internal data class TEMPLATEScreenNavigation(
-    val pop: () -> Unit,
-) : NavigationActions
+internal interface TEMPLATEScreenNavigation : NavigationActions {
+    fun TEMPLATEComponent.pop()
+}
 
 data object TEMPLATEViewState
 
 @Factory
 internal class TEMPLATEComponent(
     @InjectedParam componentContext: AppComponentContext,
     @InjectedParam override val navigation: TEMPLATEScreenNavigation,
-) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(componentContext, TEMPLATEViewState), TEMPLATEScreen {
-
-    override val actions: TEMPLATEScreen.Actions = object : TEMPLATEScreen.Actions {
-        override fun onBack() = navigation.pop()
-    }
+) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(
+    componentContext = componentContext,
+    defaultState = TEMPLATEViewState,
+), TEMPLATEScreen, TEMPLATEScreenNavigation by navigation,
path: shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt
user: login: github-actions[bot]
body: <table data-meta="generated_by_danger-pr">
  <tbody>
    <tr>
      <td>:warning:</td>
      <td width="100%" data-sticky="false"><span data-href="https://github.com/futuredapp/kmp-futured-template/blob/4c23c1b709ed3c55644e33d20f9eab853acec53f/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt#L38"></span>Unexpected indentation (0) (should be 4)</td>
    </tr>
  </tbody>
</table>




created_at: 2025-03-12T10:27:41Z
author_association: NONE
diff_hunk: @@ -22,21 +22,24 @@ interface TEMPLATEScreen {
     }
 }
 
-internal data class TEMPLATEScreenNavigation(
-    val pop: () -> Unit,
-) : NavigationActions
+internal interface TEMPLATEScreenNavigation : NavigationActions {
+    fun TEMPLATEComponent.pop()
+}
 
 data object TEMPLATEViewState
 
 @Factory
 internal class TEMPLATEComponent(
     @InjectedParam componentContext: AppComponentContext,
     @InjectedParam override val navigation: TEMPLATEScreenNavigation,
-) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(componentContext, TEMPLATEViewState), TEMPLATEScreen {
-
-    override val actions: TEMPLATEScreen.Actions = object : TEMPLATEScreen.Actions {
-        override fun onBack() = navigation.pop()
-    }
+) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(
+    componentContext = componentContext,
+    defaultState = TEMPLATEViewState,
+), TEMPLATEScreen, TEMPLATEScreenNavigation by navigation,
path: shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt
user: login: github-actions[bot]
body: <table data-meta="generated_by_danger-pr">
  <tbody>
    <tr>
      <td>:warning:</td>
      <td width="100%" data-sticky="false"><span data-href="https://github.com/futuredapp/kmp-futured-template/blob/4c23c1b709ed3c55644e33d20f9eab853acec53f/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt#L38"></span>Missing newline after ","</td>
    </tr>
  </tbody>
</table>




created_at: 2025-03-12T10:27:42Z
author_association: NONE
diff_hunk: @@ -22,21 +22,23 @@ interface TEMPLATEScreen {
     }
 }
 
-internal data class TEMPLATEScreenNavigation(
-    val pop: () -> Unit,
-) : NavigationActions
+internal interface TEMPLATEScreenNavigation : NavigationActions {
+    fun TEMPLATEComponent.pop()
+}
 
 data object TEMPLATEViewState
 
 @Factory
 internal class TEMPLATEComponent(
     @InjectedParam componentContext: AppComponentContext,
     @InjectedParam override val navigation: TEMPLATEScreenNavigation,
-) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(componentContext, TEMPLATEViewState), TEMPLATEScreen {
-
-    override val actions: TEMPLATEScreen.Actions = object : TEMPLATEScreen.Actions {
-        override fun onBack() = navigation.pop()
-    }
+) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(
+    componentContext = componentContext,
+    defaultState = TEMPLATEViewState,
+), TEMPLATEScreen, TEMPLATEScreenNavigation by navigation, TEMPLATEScreen.Actions {
path: shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt
user: login: github-actions[bot]
body: <table data-meta="generated_by_danger-pr">
  <tbody>
    <tr>
      <td>:warning:</td>
      <td width="100%" data-sticky="false"><span data-href="https://github.com/futuredapp/kmp-futured-template/blob/54f944bf2d0514e0cf8f31d73b26b7ff4bcd4070/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt#L38"></span>Unexpected indentation (0) (should be 4)</td>
    </tr>
  </tbody>
</table>




created_at: 2025-03-12T11:56:18Z
author_association: NONE
diff_hunk: @@ -22,21 +22,23 @@ interface TEMPLATEScreen {
     }
 }
 
-internal data class TEMPLATEScreenNavigation(
-    val pop: () -> Unit,
-) : NavigationActions
+internal interface TEMPLATEScreenNavigation : NavigationActions {
+    fun TEMPLATEComponent.pop()
+}
 
 data object TEMPLATEViewState
 
 @Factory
 internal class TEMPLATEComponent(
     @InjectedParam componentContext: AppComponentContext,
     @InjectedParam override val navigation: TEMPLATEScreenNavigation,
-) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(componentContext, TEMPLATEViewState), TEMPLATEScreen {
-
-    override val actions: TEMPLATEScreen.Actions = object : TEMPLATEScreen.Actions {
-        override fun onBack() = navigation.pop()
-    }
+) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>(
+    componentContext = componentContext,
+    defaultState = TEMPLATEViewState,
+), TEMPLATEScreen, TEMPLATEScreenNavigation by navigation, TEMPLATEScreen.Actions {
path: shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt
user: login: github-actions[bot]
body: <table data-meta="generated_by_danger-pr">
  <tbody>
    <tr>
      <td>:warning:</td>
      <td width="100%" data-sticky="false"><span data-href="https://github.com/futuredapp/kmp-futured-template/blob/54f944bf2d0514e0cf8f31d73b26b7ff4bcd4070/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt#L38"></span>Missing newline after ","</td>
    </tr>
  </tbody>
</table>




created_at: 2025-03-12T11:56:19Z
author_association: NONE
diff_hunk: @@ -28,6 +28,8 @@ include(":shared:feature")
 include(":shared:persistence")
 include(":shared:platform")
 include(":shared:resources")
+include(":shared:factory-generator:annotation")
+include(":shared:factory-generator:processor")
path: settings.gradle.kts
user: login: matejsemancik
body: @coderabbitai These modules are specifically tied to Decompose functionality, as their purpose is to generate Koin injection factories for Decompose components.
created_at: 2025-03-14T07:27:34Z
author_association: MEMBER
diff_hunk: @@ -28,6 +28,8 @@ include(":shared:feature")
 include(":shared:persistence")
 include(":shared:platform")
 include(":shared:resources")
+include(":shared:factory-generator:annotation")
+include(":shared:factory-generator:processor")
path: settings.gradle.kts
user: login: coderabbitai[bot]
body: <details>
<summary>🧩 Analysis chain</summary>

@matejsemancik Thank you for the clarification! I see your point now. Since these modules are specifically designed to generate Koin injection factories for Decompose components, placing them under the Decompose module hierarchy (`:shared:arkitekt-decompose:annotation` and `:shared:arkitekt-decompose:processor`) would indeed better reflect their purpose and architectural relationship.

This naming would more clearly communicate that these modules are an extension of the Decompose functionality rather than a standalone feature. I agree with keeping "processor" rather than "compiler" as it aligns better with KSP terminology.

---

🏁 Script executed:
```
#!/bin/bash
# Examine the annotation class
cat shared/factory-generator/annotation/src/commonMain/kotlin/app/futured/factorygenerator/annotation/GenerateFactory.kt
```

Length of output: 275

---

🏁 Script executed:
```
#!/bin/bash
# Check the processor implementation to understand how it works
cat shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/ComponentFactoryProcessor.kt
cat shared/factory-generator/processor/src/jvmMain/kotlin/app/futured/factorygenerator/processor/PoetFactoryComponentGenerator.kt

# Check for explicit Decompose dependencies
grep -r "Decompose" shared/factory-generator/
grep -r "import com.arkivanov.decompose" shared/factory-generator/

# See if this is tied to specific Decompose component classes
grep -r "ComponentContext" shared/factory-generator/
```

Length of output: 10092

---

🏁 Script executed:
```
#!/bin/bash
# Check usage of the annotation in components
grep -r "@GenerateFactory" shared/feature/src/commonMain/kotlin/ | head -n 10

# Check import statements in components using the annotation
grep -A 2 "import app.futured.factorygenerator.annotation.GenerateFactory" shared/feature/src/commonMain/kotlin/ | head -n 15

# Look for patterns in the component classes that use the annotation
ls -la shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/
```

Length of output: 1977

---



</details>

@matejsemancik Thanks for the clarification! After examining the code, I can see that these modules are indeed specifically designed for generating Koin injection factories for Decompose components. The processor is tightly coupled with both Koin and the Decompose component architecture.

I agree with your naming proposal:
- `shared:arkitekt-decompose:annotation`
- `shared:arkitekt-decompose:processor`

This structure better reflects the architectural relationship and purpose of these modules as extensions of the Decompose functionality rather than standalone features.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-14T07:28:34Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-02-07T11:26:03Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This pull request makes several coordinated updates across build configuration files, Kotlin source files, and an initialization script. The Kotlin extension for iOS targets is updated to use a new receiver type, shifting API context to KotlinMultiplatformExtension. In Gradle configuration, Koin-specific annotation processors are removed in favor of a more generic annotations processor, and new modules and annotations blocks are added. The project initialization script is refactored into modular utility functions for file operations and subproject renaming, with additional functionality to remove the LICENSE file. Several UI components are enhanced to use factory annotations and directly implement action interfaces, streamlining component instantiation through factory methods. New KSP-based processor classes and service registrations support the factory generation mechanism with improved error handling.

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant KSP as KSP Environment
    participant Provider as ComponentFactoryProcessorProvider
    participant Processor as ComponentFactoryProcessor
    participant Generator as PoetFactoryComponentGenerator

    KSP->>Provider: Request processor creation
    Provider->>Processor: Instantiate processor
    Processor->>Generator: Generate factory component
    Generator-->>Processor: Return generated component
    Processor-->>KSP: Complete symbol processing
```

```mermaid
sequenceDiagram
    participant User as User Interaction
    participant Root as RootNavHostComponent
    participant LoginFactory as LoginComponentFactory
    participant SignedInFactory as SignedInNavHostComponentFactory

    User->>Root: Initiates navigation event
    Root->>LoginFactory: Create LoginComponent(componentContext, rootNavigator)
    LoginFactory-->>Root: Returns LoginComponent instance
    Note over Root: On successful login
    Root->>SignedInFactory: Create SignedInNavHostComponent(componentContext, navigationToLogin, initialConfig)
    SignedInFactory-->>Root: Returns SignedInNavHostComponent instance
```

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Lite**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between f5d42ba87935906c81ab0381fc877f37fd2c3450 and 50de90c1a2e50f1294e0d81b3486eeeac9c9633a.

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt` (1 hunks)
* `shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt` (5 hunks)
* `shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginComponent.kt` (2 hunks)
* `shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/FruitPickerComponent.kt` (3 hunks)
* `shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/VegetablePickerComponent.kt` (3 hunks)
* `shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileComponent.kt` (2 hunks)
* `shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt` (4 hunks)
* `shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt` (3 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (5)</summary>

* shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileComponent.kt
* shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginComponent.kt
* shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/VegetablePickerComponent.kt
* shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/FruitPickerComponent.kt
* shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt

</details>

<details>
<summary>🔇 Additional comments (14)</summary><blockquote>

<details>
<summary>shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt (4)</summary>

`25-27`: **Good transformation from data class to interface for TEMPLATEScreenNavigation**

The change to make `TEMPLATEScreenNavigation` an interface that extends `NavigationActions` with an extension function on `TEMPLATEComponent` is a good design decision. This approach:
- Creates a cleaner separation of concerns
- Makes the navigation action `pop()` contextually bound to the component
- Follows the suggestion from a previous review comment

---

`35-41`: **Improved component architecture with explicit delegation**

The changes to `TEMPLATEComponent` constructor and inheritance are well-structured:
- Using named parameters improves readability 
- Implementing multiple interfaces with delegation pattern (`TEMPLATEScreenNavigation by navigation`)
- Directly implementing `TEMPLATEScreen` and `TEMPLATEScreen.Actions`
- Fixed indentation issues mentioned in previous commit comments

This architectural approach reduces boilerplate and creates a more cohesive component design.

---

`43-43`: **Simplified actions assignment**

The reassignment of `actions` to `this` is cleaner and more direct than using a navigation reference. This works well now that the component directly implements `TEMPLATEScreen.Actions`.

---

`46-46`: **Simplified onBack implementation**

The implementation of `onBack()` now directly calls `pop()` instead of `navigation.pop()`. This is possible and more concise because the component now inherits from `TEMPLATEScreenNavigation` through delegation.

</details>
<details>
<summary>shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt (5)</summary>

`3-3`: **Importing the new factory generation annotation**

The addition of the `GenerateFactory` import supports the new KSP-based factory generation mechanism mentioned in the PR objectives.

---

`12-12`: **Adding factory generation capability**

The `@GenerateFactory` annotation enables automatic component factory generation using KSP, which aligns with the PR objective of introducing component factory generation capabilities.

---

`23-24`: **Improved code structure with direct implementation**

The component now directly implements `ThirdScreen.Actions` interface rather than using an anonymous inner object, which improves code organization and readability.

---

`28-28`: **Simplified actions reference**

Using `this` as the actions implementation eliminates the need for an anonymous object, making the code more concise and easier to maintain.

---

`30-30`: **Clean action method implementation**

The `onBack()` method is now directly implemented in the component class, which is more straightforward than the previous anonymous object approach.

</details>
<details>
<summary>shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt (5)</summary>

`3-3`: **Importing the factory generator annotation**

The import of `GenerateFactory` annotation is consistent with the factory generation pattern being applied across components in this PR.

---

`20-20`: **Enabling factory generation**

The `@GenerateFactory` annotation aligns with the PR objective to generate component factories using KSP, reducing manual boilerplate code.

---

`31-32`: **Improved component structure with direct implementation**

Having the component directly implement `FirstScreen.Actions` and delegate navigation responsibilities using `by navigation` creates a cleaner, more cohesive class design.

---

`41-41`: **Simplified actions reference**

Returning `this` as the actions implementation eliminates the need for an anonymous object, making the code more concise and maintainable.

---

`50-50`: **Direct action method implementation**

The `onNext()` method implementation is now directly in the component class, making the code flow easier to follow compared to the previous nested anonymous object approach.

</details>

</blockquote></details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=futuredapp/kmp-futured-template&utm_content=105):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: xmadera
created_at: 2025-03-11T16:06:44Z
body: @coderabbitai review
author_association: MEMBER
user: login: coderabbitai[bot]
created_at: 2025-03-11T16:06:52Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>:white_check_mark: Actions performed</summary>

Review triggered.

> Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-12T10:27:57Z
body: <!--
  0 Errors
  5 Warnings: Feature or fix PR title should...
  0 Messages
  0 Markdowns
-->
<table>
  <thead>
    <tr>
      <th width="50"></th>
      <th width="100%" data-danger-table="true" data-kind="Warning">
          5 Warnings
      </th>
     </tr>
  </thead>
  <tbody>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">Feature or fix PR title should include JIRA-ID and short description.</td>
    </tr>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">Feature or fix PR branch name should include JIRA-ID and short description.</td>
    </tr>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">Please start subject with capital letter.<br />
f5d42ba87935906c81ab0381fc877f37fd2c3450</td>
    </tr>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">Please start subject with capital letter.<br />
4364a10ae5ca0c07de1b8eca82064f109fe44467</td>
    </tr>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">Please start subject with capital letter.<br />
8f93dae041534e681682fdb64784f14f635c6c2e</td>
    </tr>
  </tbody>
</table>

<p align="right" data-meta="generated_by_danger-pr">
  Generated by :no_entry_sign: <a href="https://danger.systems/">Danger</a>
</p>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-12T13:00:46Z
body: <!--
  0 Errors
  8 Warnings: Feature or fix PR title should...
  2 Messages: iosAppUITests: Executed 1 test...
  0 Markdowns
-->
<table>
  <thead>
    <tr>
      <th width="50"></th>
      <th width="100%" data-danger-table="true" data-kind="Warning">
          8 Warnings
      </th>
     </tr>
  </thead>
  <tbody>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">Feature or fix PR title should include JIRA-ID and short description.</td>
    </tr>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">Feature or fix PR branch name should include JIRA-ID and short description.</td>
    </tr>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">Please start subject with capital letter.<br />
f5d42ba87935906c81ab0381fc877f37fd2c3450</td>
    </tr>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">Please start subject with capital letter.<br />
4364a10ae5ca0c07de1b8eca82064f109fe44467</td>
    </tr>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">Please start subject with capital letter.<br />
8f93dae041534e681682fdb64784f14f635c6c2e</td>
    </tr>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">KLIB resolver: The same 'unique_name=annotation_commonMain' found in more than one library: /Users/runnerm1/actions-runner/_work/kmp-futured-template/kmp-futured-template/shared/factory-generator/annotation/build/classes/kotlin/metadata/commonMain, /Users/runnerm1/actions-runner/_work/kmp-futured-template/kmp-futured-template/shared/feature/build/kotlinTransformedMetadataLibraries/nativeMain/androidx.annotation-annotation-1.7.0-commonMain-kNhS6A.klib, /Users/runnerm1/actions-runner/_work/kmp-futured-template/kmp-futured-template/shared/feature/build/kotlinTransformedMetadataLibraries/nativeMain/org.jetbrains.compose.annotation-internal-annotation-1.7.0-commonMain-BD0V2w.klib</td>
    </tr>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">KLIB resolver: The same 'unique_name=resources_commonMain' found in more than one library: /Users/runnerm1/actions-runner/_work/kmp-futured-template/kmp-futured-template/shared/feature/build/kotlinTransformedMetadataLibraries/commonMain/dev.icerock.moko-resources-0.24.0-commonMain-3vAcDQ.klib, /Users/runnerm1/actions-runner/_work/kmp-futured-template/kmp-futured-template/shared/resources/build/classes/kotlin/metadata/commonMain</td>
    </tr>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">KLIB resolver: The same 'unique_name=runtime_commonMain' found in more than one library: /Users/runnerm1/actions-runner/_work/kmp-futured-template/kmp-futured-template/shared/feature/build/kotlinTransformedMetadataLibraries/commonMain/org.jetbrains.compose.runtime-runtime-1.7.0-commonMain-9pDeVQ.klib, /Users/runnerm1/actions-runner/_work/kmp-futured-template/kmp-futured-template/shared/feature/build/kotlinTransformedMetadataLibraries/nativeMain/app.cash.sqldelight-runtime-2.0.1-commonMain-wyRdBg.klib</td>
    </tr>
  </tbody>
</table>
<table>
  <thead>
    <tr>
      <th width="50"></th>
      <th width="100%" data-danger-table="true" data-kind="Message">
          2 Messages
      </th>
     </tr>
  </thead>
  <tbody>
    <tr>
      <td>:book:</td>
      <td data-sticky="false">iosAppUITests: Executed 1 test, with 0 failures (0 expected) in 18.118 (18.118) seconds</td>
    </tr>
    <tr>
      <td>:book:</td>
      <td data-sticky="false">iosAppTests: Executed 1 test, with 0 failures (0 expected) in 0.001 (0.001) seconds</td>
    </tr>
  </tbody>
</table>

<p align="right" data-meta="generated_by_danger">
  Generated by :no_entry_sign: <a href="https://danger.systems/">Danger</a>
</p>

author_association: NONE