Nucleus
Tao backend

Migrate from JBR to Tao

What changes when you switch the backend from JetBrains Runtime to Tao — behavior, perf, and when to keep JBR.

If you're on Nucleus 1.x with decorated-window-jbr (or decorated-window-jni), the path to Tao is a dependency swap plus a backend flag. The DecoratedWindow API is the same — the bytes underneath are different.

TL;DR

  • Swap decorated-window-jbr (or -jni) for decorated-window-tao and pass backend = NucleusBackend.Tao.
  • AWT is gone — no more ComposeWindow, no Swing EDT. nucleusWindow.unsafe.taoWindow replaces unsafe.awtWindow.
  • Resident memory drops from ~120 MB to ~60 MB on a Hello World; cold start halves on GraalVM native-image.
  • Things to retest: drag-and-drop, deep links, file dialogs, native overlays.
  • Keep JBR when your app pulls in Swing widgets or third-party AWT components.

Switch the dependency

build.gradle.kts
dependencies {
    implementation("dev.nucleusframework:nucleus.nucleus-application:<version>")
    implementation("dev.nucleusframework:nucleus.decorated-window-jbr:<version>")
}
nucleusApplication {
    DecoratedWindow(onCloseRequest = ::exitApplication, title = "App") {
        TitleBar { state -> /* … */ }
        MyContent()
    }
}
build.gradle.kts
dependencies {
    implementation("dev.nucleusframework:nucleus.nucleus-application:<version>")
    implementation("dev.nucleusframework:nucleus.decorated-window-tao:<version>")
}
nucleusApplication(backend = NucleusBackend.Tao) {
    DecoratedWindow(onCloseRequest = ::exitApplication, title = "App") {
        TitleBar { state -> /* … */ }
        MyContent()
    }
}

If you keep both backends on the classpath, NucleusBackend.Auto picks Tao when it's available.

Behaviour differences

ConcernJBR / JNITao
Underlying windowComposeWindow (extends JFrame)TaoWindow (Rust handle)
Event dispatchAWT EDTTao event loop on main thread
unsafe.awtWindowNon-nullNull (use unsafe.taoWindow)
Native file dialogsVia AWT/SwingVia file-dialog module
Drag-and-dropAWT TransferHandlerTaoDragAndDropPayload
Deep linksAWT Apple Event handler / AppKitTaoDeepLinkBridge.setSink
WaylandXWaylandNative
-XstartOnFirstThreadNot requiredRequired on macOS (plugin handles it)
Resident memory (Hello World)~120 MB~60 MB
GraalVM native-imagePartialFull

What to retest

  • Drag-and-drop: rewrite handlers against TaoDragAndDropPayload.
  • Deep links: register through nucleusApplication.onDeepLink { uri -> … } — the underlying bridge changes but the API doesn't.
  • File pickers: replace any direct JFileChooser calls with the cross-backend module.
  • Native overlays (SwiftUI/WebView2/GTK): now possible via NativeView. You may want to migrate previously embedded AWT canvases here.
  • Window placement on Linux: Wayland compositors may ignore absolute positions. See Wayland.
  • Multi-window flows: still one nucleusApplication { }, multiple DecoratedWindow calls. Each gets a NucleusWindow.

Performance impact

  • Cold start drops by ~40% on JVM, ~60% on GraalVM native-image (no AWT init).
  • Resident memory halves on a baseline app.
  • Frame timing is steadier on Linux Wayland (no XWayland blit step).
  • Trade-off: the Tao renderer is newer than the AWT path. Expect a smaller surface of edge cases to discover.

When to keep JBR

  • You ship a Swing-based legacy module.
  • You embed an AWT Canvas (e.g. for video) and haven't ported it to NativeView yet.
  • You use a library that requires ComposeWindow directly.

You can ship both — Nucleus 2.0 publishes decorated-window-jbr, decorated-window-jni and decorated-window-tao simultaneously. Use NucleusBackend.Awt or NucleusBackend.Tao to pin the choice per build, or Auto to detect.