Skill: Compose Multiplatform (CMP) UI
SkillDev toolsLets your agent work with the Meshtastic Android app, an open-source client for off-grid mesh radio messaging.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Skill: Compose Multiplatform (CMP) UI skill
About this capability
Android application for Meshtastic
What this skill tells your AI
The instructions your AI receives, as published by meshtastic/meshtastic-android in .skills/compose-ui/SKILL.md and read by ahel’s review.
Description
Guidelines for building shared UI, adaptive layouts, and handling strings/resources in Meshtastic-Android. The codebase uses Material 3 Adaptive.
1. UI Components & Layouts
- Material 3 / Adaptive: Use
currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true)to support Large (1200dp) and XL (1600dp) breakpoints. Investigate 3-pane "Power User" scenes using Navigation 3 Scenes and draggable dividers for desktopApp/tablets. - Dialogs & Alerts: Use centralized components like
AlertHost(alertManager)fromcore:ui/commonMain. Do NOT trigger alerts inline or duplicate alert logic. UseSharedDialogs(uiViewModel)for general popups. - Placeholders: Use
PlaceholderScreen(name)fromcore:ui/commonMainfor unimplemented desktopApp/JVM features. - Theme Picker: Use
ThemePickerDialogfromfeature:settings/commonMain. - Platform Implementations: Inject platform-specific behavior (e.g., Map providers) via
CompositionLocalfrom theandroidAppordesktopAppshells. Do not tightly couple Google Maps dependencies tocommonMain; the MapLibre surfaces live in:feature:map-maplibre, not in acoremodule.
2. Strings & Resources
- Multiplatform Resources: MUST use
core:resources(e.g.,stringResource(Res.string.your_key)). Never use hardcoded strings. - ViewModels/Coroutines: Use the asynchronous
getStringSuspend(Res.string.your_key). NEVER use blockinggetString()in a coroutine context. - Formatting Constraints: CMP
stringResourceonly supports%N$s(string) and%N$d(integer).- No Float formatting: Formats like
%N$.1fpass through unsubstituted. Pre-format in Kotlin usingNumberFormatter.format(value, decimalPlaces)fromcore:commonand pass as a string argument (%N$s):val formatted = NumberFormatter.format(batteryLevel, 1) // "73.5" stringResource(Res.string.battery_percent, formatted) // uses %1$s - Percent Literals: Use bare
%(not%%) for literal percent signs in CMP-consumed strings.
- No Float formatting: Formats like
String Formatting Decision Tree
Choose the right tool for the job:
| Scenario | Tool | Example |
|---|---|---|
| Metric display (temp, voltage, %, signal) | MetricFormatter.* | MetricFormatter.temperature(25.0f, isFahrenheit) → "77.0°F" |
| Simple number + unit | NumberFormatter + interpolation | "${NumberFormatter.format(val, 1)} dB" |
| Localized template from strings.xml | stringResource(Res.string.key, preFormattedArgs) | stringResource(Res.string.battery, formatted) |
| Non-composable template (notifications, plain functions) | formatString(template, args) | formatString(template, label, value) |
| Hex formatting | formatString | formatString("!%08x", nodeNum) |
| Date/time | DateFormatter | DateFormatter.format(instant) |
Rules:
- NEVER use
%.Nfin strings.xml — CMP cannot substitute them. Use%N$sand pre-format floats. - Prefer
MetricFormatterover scatteredformatString("%.1f°C", temp)calls. formatString(pure Kotlin) is a pure-KotlincommonMainimplementation for: hex formats, multi-arg templates fetched at runtime, and chart axis formatters. Located incore:commonFormatter.kt.NumberFormatteralways uses.as decimal separator — intentional for mesh networking precision.
- Workflow to Add a String:
- Add to
core/resources/src/commonMain/composeResources/values/strings.xml. - Run
python3 scripts/sort-strings.py— keeps the file sorted and regeneratesstrings-index.txt. - Use the generated
org.meshtastic.core.resources.<key>symbol. - Validate UI presentation.
- Add to
3. Tooling & Capabilities
- Image Loading: Use
libs.coil(Coil Compose) in feature modules. Configuration/Networking for Coil (coil-network-ktor3) happens strictly in theandroidAppanddesktopApphost modules. - QR Codes: Use
rememberQrCodePainterfromcore:ui/commonMainpowered byqrcode-kotlin. No ZXing or Android Bitmap APIs in shared code.
4. Compose Previews
- Preview in commonMain: CMP 1.11+ supports
@PreviewincommonMainviacompose-multiplatform-ui-tooling-preview. Place preview functions alongside their composables. - Import: Use
androidx.compose.ui.tooling.preview.Preview. The JetBrains-prefixed import (org.jetbrains.compose.ui.tooling.preview.Preview) is deprecated.
5. Dialog & State Patterns
- Dialog State Preservation: Use
rememberSaveablefor dialog state (search queries, selected tabs, expanded flags) to preserve across configuration changes. Boolean and String types are auto-saveable — no customSaverneeded.
6. Driving the running desktop app
CMP 1.12+ ships an MCP server inside Compose Hot Reload; .mcp.json registers it as compose-hot-reload (:desktopApp:hotMcpServer). With ./gradlew :desktopApp:hotRun running, it drives the live app — inspect, input and reload without a rebuild.
- Tools:
status,reload,await_reload,get_semantic_tree,click,type_text,scroll,get_logs,get_ui_error,take_screenshot. - Poll
statusuntilconnected: truebefore anything else — the server accepts requests before the app has connected to it. get_semantic_treeis the assertion surface: roles, text,selected/focused, available actions and bounds.clickaddresses nodes bynodeIdtaken from that tree. Prefer it overtake_screenshot, whose output depends on the host renderer.reloadafter editing sources applies the change into the running app; useawait_reloadinstead when the app was started with--auto.
Reference Anchors
- Shared Strings:
core/resources/src/commonMain/composeResources/values/strings.xml - Platform abstraction contract:
core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/MapViewProvider.kt - Provider wiring:
androidApp/src/main/kotlin/org/meshtastic/app/MainActivity.kt
Signals
- GitHub stars
- 2k
- Forks
- 512
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
compose-ui-meshtastic- Source
- github.com/meshtastic/meshtastic-android