:desktop — Meshtastic Desktop
A Compose Desktop application target — the first full non-Android target for the shared KMP module graph. This module serves as:
- First multi-target milestone — Proves the KMP architecture supports real application targets beyond Android.
- Build smoke-test — Validates that all
core:*KMP modules compile and link on a JVM Desktop target. - Shared navigation proof — Uses the same Navigation 3 routes from
core:navigationand the sameNavDisplay+entryProviderpattern as the Android app, proving the shared backstack architecture works cross-target. - Desktop app scaffold — A working Compose Desktop application with a
NavigationRailfor top-level destinations and placeholder screens for each feature.
Quick Start
# Run the desktop app
./gradlew :desktop:run
# Run tests
./gradlew :desktop:test
# Package native distribution (DMG/MSI/DEB) — debug (no ProGuard)
./gradlew :desktop:packageDistributionForCurrentOS
# Package native distribution (DMG/MSI/DEB) — release (ProGuard minified)
./gradlew :desktop:packageReleaseDistributionForCurrentOS
ProGuard / Minification
Release builds use ProGuard for tree-shaking (unused code removal), significantly reducing distribution size. Obfuscation is disabled since the project is open-source.
Configuration:
build.gradle.kts—buildTypes.release.proguardblock enables ProGuard withoptimize.set(true)andobfuscate.set(false).proguard-rules.pro— Comprehensive keep-rules for all reflection/JNI-sensitive dependencies (Koin, kotlinx-serialization, Wire protobuf, Room KMP, Ktor, Kable BLE, Coil, SQLite JNI, Compose Multiplatform resources).
Troubleshooting ProGuard issues:
- If the release build crashes at runtime with
ClassNotFoundExceptionorNoSuchMethodError, a library is loading classes via reflection that ProGuard stripped. Add a-keeprule inproguard-rules.pro. - To debug which classes ProGuard removes, temporarily add
-printusage proguard-usage.txtto the rules file and inspect the output indesktop/proguard-usage.txt. - To see the full mapping of optimizations applied, add
-printseeds proguard-seeds.txt. - Run
./gradlew :desktop:runReleasefor a quick smoke-test of the minified app before packaging.
Architecture
The module depends on the JVM variants of KMP modules:
core:common,core:model,core:di,core:navigation,core:repositorycore:domain,core:data,core:database,core:datastore,core:prefscore:network,core:resources,core:ui
Navigation: Uses JetBrains multiplatform forks of Navigation 3 (org.jetbrains.androidx.navigation3:navigation3-ui) and Lifecycle (org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose, lifecycle-runtime-compose). A unified SavedStateConfiguration with polymorphic SerializersModule is provided centrally by core:navigation for non-Android NavKey serialization. Desktop utilizes the exact same navigation graph wiring (settingsGraph, nodesGraph, contactsGraph, connectionsGraph) directly from the commonMain of their respective feature modules, maintaining full UI parity.
Coroutines: Requires kotlinx-coroutines-swing for Dispatchers.Main on JVM/Desktop. Without it, any code using lifecycle.coroutineScope or Dispatchers.Main (e.g., NodeRepositoryImpl, RadioConfigRepositoryImpl) will crash at runtime.
DI: A Koin DI graph is bootstrapped in Main.kt with platform-specific implementations injected.
UI: JetBrains Compose for Desktop with Material 3 theming. Desktop acts as a thin host shell, delegating almost entirely to fully shared KMP UI modules. Includes native macOS notification support (via TrayState and bundleID identification) and a monochrome SVG tray icon for a native look and feel.
Notifications: Implements the common NotificationManager interface via DesktopNotificationManager. Repository-level notifications (messages, node events, alerts) are collected in Main.kt and forwarded to the system tray. macOS requires a consistent bundleID (configured in build.gradle.kts) and the NSUserNotificationAlertStyle key in Info.plist for notifications to appear correctly in the distributable.
Localization: Desktop exposes a language picker, persisting the selected BCP-47 tag in UiPreferencesDataSource.locale. Main.kt applies the override to the JVM default Locale and uses a staticCompositionLocalOf-backed recomposition trigger so Compose Multiplatform stringResource() calls update immediately without recreating the Navigation 3 backstack.
Key Files
| File | Purpose |
|---|---|
Main.kt |
App entry point — Koin bootstrap, Compose Desktop window, theme + locale application |
DemoScenario.kt |
Offline demo data for testing without a connected device |
ui/DesktopMainScreen.kt |
Navigation 3 shell — NavigationRail + NavDisplay |
navigation/DesktopNavigation.kt |
Nav graph entry registrations for all top-level destinations (delegates to shared feature graphs) |
radio/DesktopRadioTransportFactory.kt |
Provides TCP, Serial/USB, and BLE transports |
notification/DesktopMeshServiceNotifications.kt |
Real implementation of notification triggers for Desktop |
DesktopNotificationManager.kt |
Bridge between repository notifications and Compose TrayState |
radio/DesktopMeshServiceController.kt |
Mesh service lifecycle — orchestrates want_config handshake chain |
radio/DesktopMessageQueue.kt |
Message queue for outbound mesh packets |
di/DesktopKoinModule.kt |
Koin module with stub implementations |
di/DesktopPlatformModule.kt |
Platform-specific Koin bindings |
stub/NoopStubs.kt |
No-op implementations for all repository interfaces |
What This Validates
| Module | What's Tested |
|---|---|
core:common |
Base64Factory, NumberFormatter, UrlUtils, DateFormatter, CommonUri |
core:model |
DeviceVersion, Capabilities, SfppHasher, platformRandomBytes, getShortDateTime, Channel.getRandomKey |
core:ui |
Shared Compose components compile and render on Desktop |
| Build graph | All core modules compile and link without Android SDK |
Roadmap
- Implement real navigation with shared
core:navigationroutes (Navigation 3 shell) - Adopt JetBrains multiplatform forks for lifecycle and navigation3
- Implement native macOS/Desktop notification support with
TrayStateand system tray - Wire
feature:settingscomposables into the nav graph (first real feature — ~30 screens) - Wire
feature:nodecomposables into the nav graph (node list with shared ViewModel + NodeItem) - Wire
feature:messagingcomposables into the nav graph (contacts list with shared ViewModel) - Add JetBrains Material 3 Adaptive
ListDetailPaneScaffoldto node and messaging screens - Implement TCP transport (
DesktopRadioTransportFactory) with auto-reconnect and backoff retry - Implement mesh service controller (
DesktopMeshServiceController) with fullwant_confighandshake - Create connections screen using shared
feature:connectionswith dynamic transport detection - Replace 5 placeholder config screens with real desktop implementations (Device, Position, Network, Security, ExtNotification)
- Add desktop language picker backed by shared
UiPreferencesDataSource.localewith live translation updates - Wire remaining
feature:*composables (map) into the nav graph - Move remaining node detail and message composables from
androidMaintocommonMain - Add serial/USB transport for direct radio connection on Desktop
- Add BLE transport (via Kable) for direct radio connection on Desktop
- Add MQTT transport for cloud-connected operation
- Package as native distributions (DMG, MSI, DEB) via CI release pipeline