All notable changes to mob_new (the project generator for Mob) are documented here.
Format: Keep a Changelog. Versioning: SemVer.
Full module documentation: hexdocs.pm/mob_new.
[0.3.8]
Added
project_swift_sourcesbuild hook on iOS templates. Bothios/build.zig.eexandios/build_device.zig.eexnow accept-Dproject_swift_sources=<absolute,paths>— a comma-separated list of extra Swift sources to compile into the sameswiftcinvocation as Mob's bridge sources. Empty/unset is a no-op. Paired withmob_dev's:project_swift_sourcesmob.exs key (mob_dev#6) so downstream apps can ship project Swift without patching the generator. Originally proposed by @dl-alexandre.MobBridge.kt.eex:MobTextFieldnow honourssecure: true. AppliesPasswordVisualTransformation()to mask input and overrides the keyboard type toKeyboardType.Password(autocorrect off, no suggestions strip). Mirrors the iOS-sidesecureprop landing in mob 0.6.x. The Elixir cleartext still reaches the BEAM viaon_changeso apps hash/store the value as normal.Existing apps generated from prior templates are unaffected — the prop is a no-op there. Regenerating or hand-porting
MobBridge.ktenables masking.
[0.3.7]
Added
- MaterialTheme follows BEAM-side
Mob.Themeout of the box. Apps generated frommix mob.newnow wire Compose's MaterialTheme to the BEAM-pushed theme so Material 3 system widgets (NavigationBar, Button, …) match whateverMob.Theme.set(...)the host has active — no more white-on-light NavigationBar over a dark Obsidian / ObsidianGlass page. Two pieces:MobBridge.kt:setTheme(json)JNI handler called bymob 0.6.14's new:mob_nif.set_theme/1. Decodes the resolved palette JSON into amutableStateOf<Map<String, Long>?>(Compose-observable; cross-thread-safe via a main-looper hop).MainActivity.kt: readsMobBridge.themeColorsand buildsdarkColorScheme(...)from it. Mob'ssurface_raised/mutedmap onto Material 3'ssurfaceVariant/onSurfaceVariant(same role). StockdarkColorScheme()covers the brief gap betweensetContentand the BEAM's first theme push.
- Requires
mob ≥ 0.6.14. Older runtimes don't define thesetThemeBridge method or call the NIF, so MaterialTheme just stays on the fallback — no breakage, but the system widgets won't followMob.Theme.set/1.
[0.3.6]
Added
Mob.Camera.start_frame_stream/2Android support baked into the template. Apps generated frommix mob.newnow ship the Kotlin side (camera_start_frame_stream,camera_stop_frame_stream,deliverFrame,centerCropAndScale,bitmapToRgbF32,bitmapToBgraU8) plus the JNI thunk (nativeDeliverCameraFrame) wired through tomob_deliver_camera_frame. Prior to this, projects generated frommob_new≤ 0.3.5 would fail at NIF load againstmob ≥ 0.6.8— the new JNI bindings forcamera_start_frame_stream/camera_stop_frame_streamarecacheRequired, so missing static methods caused the app to crash on launch.aspect_ratiomodifier prop innodeModifier(Android) — wrapsModifier.aspectRatio(r). Useful for locking camera + canvas overlays to a 1:1 square so model-space coordinates align with the visible preview area.
Fixed
MobCameraPreviewZ-order and stability fixes so Compose overlays (status text, bounding boxes, etc.) drawn on top of the camera actually render:- Switched
PreviewViewfrom defaultImplementationMode.PERFORMANCE(SurfaceView, punches through above Compose and hides overlays) toCOMPATIBLE(TextureView, renders inside the normal Compose Z-order). - Moved the camera bind out of
AndroidView.update(which re-runs on every recomposition and caused continualunbindAll/bindToLifecyclecycles whenever any sibling state ticked — e.g. an FPS counter — making the surface flicker and fight with overlays) into aLaunchedEffect(frameActive, cameraSelector)keyed only on values that should trigger a rebind. - Added
Modifier.clipToBounds()to theAndroidViewwrapping thePreviewViewso the surface texture can't bleed past its declared layout bounds. PreviewView.scaleType = FILL_CENTERto match the model-side center-crop inMobBridge.deliverFrame, so overlay-canvas coords align with the preview underneath.
- Switched
[0.3.5]
Fixed
beam_jni.c.eex: restored the closing}fornativeDeliverVendorUsbEvent. Without it, clang sees nested function definitions and rejects everyJNIEXPORTthat follows ("function definition is not allowed here") — C compilation fails immediately on any project generated from 0.3.3/0.3.4.MobBridge.kt.eex: removed three duplicate imports (IntentFilter,ConcurrentHashMap,AtomicInteger). Each now appears exactly once in the alphabetised bottom-of-file import block. kotlinc was rejecting with "Conflicting import" sogradleDebugfailed on any project generated from 0.3.3/0.3.4.beam_jni.c.eex: removed a 276-line duplicate Bluetooth Classic JNI block (lines 533-808 mirrored 256-531 verbatim). Caused "redefinition ofJava_..._MobBridge_nativeDeliverBt*" errors at compile time. The canonical first block stays. (Surfaced by the newclang -fsyntax-onlytest below — string-match generator-test assertions still passed because the substrings exist, just twice.)
All three regressions were introduced by the 0.3.3 BT-PR merge taking the union of imports / blocks during conflict resolution, when the 0.3.2 fix had specifically de-duplicated them. Reported by external user on master 2026-05-17.
Added
MobNew.Templates.Lint— module of structural lints for generator-rendered native source files. 8 checks:balanced_braces,balanced_parens,balanced_brackets,no_eex_leaks,unique_kotlin_imports,unique_swift_imports,external_fun_jni_consistency, pluscheck_kotlin/1/check_c/1/check_swift/1aggregate functions. Returns a list of issue maps; empty list = clean. 25 unit tests cover each check red-then-green.- Generator tests now use
Lint.check_kotlin/1andLint.check_c/1instead of inline brace-counting + import-dedup logic. Single source of truth; clearer failure messages. - New cross-file consistency test: asserts every
external fun nativeFoo(...)in MobBridge.kt has a matchingJava_..._MobBridge_nativeFoo(...)JNI thunk in beam_jni.c. Catches "added the Kotlin side but forgot the C thunk" (or the reverse). - New
clang -fsyntax-onlytest (@tag :requires_android_ndk) that invokes the NDK's clang against the renderedbeam_jni.c. Catches the full class of "actually broken C" — typos, wrong arg counts, duplicate definitions — that tier-1 structural lints can miss. Skips cleanly when the Android NDK isn't installed (mirrors the existing:requires_zigpattern).
[0.3.4]
Added
CLAUDE.md"Release flow" section pointing at the canonical process inmob/RELEASE.md(URL form so it resolves without a local mob checkout). mob_new specifics: generator tests needMOB_DIR=/Users/kevin/code/mobset when running from a worktree (the resolver looks formobalongside the project and the worktree path breaks that assumption)..githooks/pre-push— same script shipped in mob (cheap preflight always, release preflight whenmix.exschanged). Activate per clone or worktree withgit config core.hooksPath .githooks.
[0.3.3]
Added
- Bluetooth Classic peripheral codegen (
MobBridge.kt.eex,beam_jni.c.eex,AndroidManifest.xml.eex) — generated apps now include the Kotlin BroadcastReceivers, JNI native_* externs, and Android permissions for theMob.Btruntime API (HFP / SPP / HID). Companion tomob0.6.5'sMob.Btmodule. Contributed by @HeroesLament (#4).
[0.3.2]
Fixed
- HexDocs
source_urlandsource_url_patternpointed at the wrong repo (mobinstead ofmob_new) and at a non-existent/mob_new/subdirectory prefix; the rendered</>glyphs all 404'd. Corrected togithub.com/genericjam/mob_new/blob/master/.... - Template fix:
beam_jni.c.eexwas missing the closing}fornativeDeliverVendorUsbEventbefore the BT JNI thunks began — every subsequentJNIEXPORT void JNICALLwas rejected by clang with "function definition is not allowed here". Generator tests never caught this because they grep rendered output, not compile it. - Template fix:
MobBridge.kt.eexduplicated three imports (IntentFilter,ConcurrentHashMap,AtomicInteger) alongside the BT Bluetooth* imports; kotlinc rejected with "Conflicting import". - Template fix:
MobBridge.kt.eexmissingandroidx.compose.foundation.layout.fillMaxSizeimport for the GpuView compile-error overlay. - Template fix: orphan comment in the import block confused ktlint's
import-orderingrule (no autocorrect available when imports are interleaved with comments).
Added
.github/workflows/test.yml— runsmix test,mix format --check-formatted,mix credo --strict, andmix deps.auditon push to master and on every PR..github/workflows/release.yml— on tag push, creates a GitHub Release whose body is the matching## [X.Y.Z]section from this changelog.
[0.3.1]
Added
- Bluetooth Classic template scaffolding:
MobBridge.kt.eexgains the Kotlin BroadcastReceivers,external fun nativeDeliver*JNI declarations, and Compose wiring for theMob.Btruntime API (HFP / SPP / HID).AndroidManifest.xml.eexgains the matching modern + legacy Bluetooth permissions.beam_jni.c.eexgains the per-event JNI thunks. (Generator tests cover the rendered template's external strings; manual on-device verification per the CLAUDE.md convention.) Mob.GpuViewAndroid backend (GLES 3.0):MobBridge.kt.eexgainsMobGpuViewcomposable +MobGpuSurfaceView+MobGpuRenderer, mirroring the iOSMobGpuView.swiftshipped in mob 0.6.4. Same%{ios: "...MSL...", android: "...GLSL ES..."}cross-platform shader contract; std140-ish uniform packing matches the iOS Swift packer (scalar/vec2/vec4 with natural alignment). Translucent red compile-error overlay on shader failure, matching iOS behavior.- Generator-test coverage for both surfaces — asserts the rendered template contains the expected composables, classes, imports, and dispatch entries.
[0.3.0] and earlier
Earlier releases predate this changelog; consult the tag list and the per-tag commit messages for history.