Nucleus
Get started

Project setup

Repositories, Gradle properties, JDK toolchains and multi-module layout for a real-world Nucleus project.

The quickstart fits in one module. Real projects split UI, business logic and platform glue — and Nucleus's à la carte modules expect a layout where dependencies flow in one direction.

TL;DR

  • Resolve the plugin from Gradle Plugin Portal; resolve runtime modules from Maven Central.
  • Pin a JDK toolchain (17 minimum; 25 for AOT cache).
  • A typical layout is :app (Nucleus entry point) → :ui (Compose) → :domain (pure Kotlin), with platform integration code grouped where it's used.

Repositories

// settings.gradle.kts
pluginManagement {
    repositories {
        gradlePluginPortal()
        mavenCentral()
        google()
    }
}

dependencyResolutionManagement {
    repositories {
        mavenCentral()
        google()
    }
}

google() is needed only if you pull Compose Multiplatform from there or share modules with Android.

Plugin and runtime versions

// build.gradle.kts (app module)
plugins {
    kotlin("jvm") version "2.0.21"
    id("org.jetbrains.compose") version "1.7.0"
    id("dev.nucleusframework") version "2.0.0"
}

All dev.nucleusframework:nucleus.* runtime artifacts publish the same version. Pin once:

val nucleusVersion = "2.0.0"

dependencies {
    implementation(compose.desktop.currentOs)
    implementation("dev.nucleusframework:nucleus.nucleus-application:$nucleusVersion")
    implementation("dev.nucleusframework:nucleus.decorated-window-tao:$nucleusVersion")
    // pull in only what you need
    implementation("dev.nucleusframework:nucleus.notification-common:$nucleusVersion")
    implementation("dev.nucleusframework:nucleus.global-hotkey:$nucleusVersion")
}

nucleus-application is the umbrella; it transitively pulls core-runtime, aot-runtime and graalvm-runtime. Every other module is opt-in.

JDK toolchain

// gradle/libs.versions.toml or directly:
kotlin {
    jvmToolchain {
        languageVersion.set(JavaLanguageVersion.of(25))
        // Optional: pin the vendor for reproducible builds
        // vendor.set(JvmVendorSpec.JETBRAINS)
    }
}

Use JDK 17 if you don't need the AOT cache, JDK 25 to enable it (enableAotCache = true in nativeDistributions). For GraalVM native images, configure nucleus.application.graalvm { javaLanguageVersion.set(25) } — see Configuration.

org.gradle.jvmargs=-Xmx4g -XX:+UseG1GC
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true

kotlin.code.style=official

Configuration cache works with Nucleus's plugin; if you hit a snag, file an issue.

Multi-module layout

A pragmatic split:

my-app/
├── settings.gradle.kts
├── build.gradle.kts             ← shared conventions, no plugins
├── app/                          ← Nucleus entry point + main()
│   └── build.gradle.kts          ← applies dev.nucleusframework
├── ui/                           ← Compose Composables, themes
│   └── build.gradle.kts
└── domain/                       ← pure Kotlin: models, repositories, services
    └── build.gradle.kts

Rules:

  • Only :app applies dev.nucleusframework. Only :app declares nucleus.application { … }.
  • :domain stays pure Kotlin and can be shared with Android/iOS later (move it under a KMP commonMain source set when that day comes).
  • Platform-specific glue (e.g. a macOS dock menu, a Windows jump list) goes next to the code that uses it — most often inside :app.

Multiplatform sharing

Nucleus's runtime modules are JVM-only, but the :domain and :ui modules can be Kotlin Multiplatform without changing anything in :app. The JVM target of those modules is what :app actually consumes.

Hot Reload

Compose Hot Reload is supported out of the box — Nucleus extends the Compose plugin rather than replacing it. The mainClass you set in nucleus.application is propagated to compose.desktop.application.mainClass, so hotRun resolves the entry point with no extra config.

Next