All notable changes to mob are documented here.
Format: Keep a Changelog. Versioning: SemVer.
Full module documentation: hexdocs.pm/mob.
[0.6.11]
Fixed
~MOBsigil no longer double-encodes non-ASCII bytes in template source. The NimbleParsec parser usedascii_string/2for string attribute values (text="...") and brace content (text={...}); itsinteger-typed body re-encoded each source byte ≥128 as a Latin-1 codepoint then UTF-8. Net effect:–(E2 80 93) emerged asÂ+pad+O(C3 A2 C2 80 C2 93) — mojibake on screen. Swapped both call sites toutf8_string/2, which matches by codepoint and round-trips multi-byte sequences (em-dash, en-dash, middle dot, smart quotes, accents, emoji) byte-for-byte. Workaround that's now unnecessary: binding the non-ASCII string to a variable outside the sigil and referencing it viatext={var}.
[0.6.10]
Added
- iOS BEAM startup honours
MOB_NODE_SUFFIXenv var. The simulator branch already auto-derived a unique node-name suffix fromSIMULATOR_UDIDso concurrent sims didn't collide in Mac's EPMD, but there was no manual override path — the Android-sideMOB_NODE_SUFFIXconvention was iOS-blind. Now both branches (simulator + physical device) readMOB_NODE_SUFFIXwith priority: explicit env → SIMULATORUDID-derived (sim only) → none. Pairs withmob_dev 0.5.10'smix mob.deploy --node-suffix Xflag (forwarded to simctl via the `SIMCTL_CHILD*` mechanism). - Resolves the
Protocol 'inet_tcp': register/listen error: no_reg_reply_from_epmdsymptom seen when running multiple iOS sims of the same app concurrently for visual-comparison work (e.g. cross-platform theme parity).
[0.6.9]
Fixed
- CI pipeline unblocked. The 0.6.8 push failed two CI gates and never
reached Hex; this release ships the same code with the gates green:
android/jni/mob_beam.hreformatted to satisfyxcrun clang-format --dry-run -Werror(the camera-frame delivery declaration was split across three lines in a style clang-format wanted on two).decimalbumped 2.4.0 → 3.1.0 (transitive viaecto_sqlite3/jason) to clear advisory GHSA-rhv4-8758-jx7v — unbounded exponent inDecimal.new/1enables an unauthenticated DoS, affects< 3.0.0.jasonbumped 1.4.4 → 1.4.5 since older Jason cappeddecimalto~> 1.0 or ~> 2.0.
No source-level changes since 0.6.8 — same Mob.Camera.start_frame_stream/2
Android implementation and Mob.Canvas viewport docs, now actually on Hex.
[0.6.8]
Added
Mob.Camera.start_frame_stream/2now works on Android. The Camera2 + CameraXImageAnalysisuse case is wired through to BEAM as{:camera, :frame, %{bytes, width, height, format, timestamp_ms, dropped}}messages. Previously this NIF returned:unsupportedon Android — iOS-only. The Android implementation supports the sameformat: :rgb_f32the iOS side does (:bgra_u8planned for a follow-up).Mob.Canvasmoduledoc documents the viewport-scaling contract: thewidth/heightprops are logical viewport units, NOT pixels. The renderer scales draw-op coordinates against the actual on-screen pixel size. New tests intest/mob/canvas_test.exspin the contract so future readers don't regress to interpreting them as raw pixels.
Notes
- Combined with
mob_dev 0.5.9'smix mob.enable tfliteand thenx_tflite_mob 0.0.3Hex package, the cross-platform live YOLO demo (mob_yolo_demo) now runs end-to-end with only Hex deps. Measured perf: 24 ms iPhone SE A15 via Core ML → ANE; 75–117 ms Moto G Power 5G (Dimensity / BXM-8-256) via NNAPI /mtk-gpu_shim.
[0.6.7]
Added
guides/mobile_surface_matrix.md— comprehensive audit of mob's mobile capability surface vs. React Native + Expo SDK reference. Tables across UI components, gestures/input, device/system, storage, camera/audio, connectivity, sensors, location, notifications, background tasks, auth/payment, ML/Vision, maps, accessibility, iOS-only, Android-only, plus an "architecturally not present" section. Per-row status (✅ / 🟡 / ❌ / ⛔) with iOS + Android indicators. Hand-maintained from inspection oflib/mob/andsrc/mob_nif.erl. Sets realistic expectations and surfaces plugin candidates.- README link + hexdocs entry so the matrix is discoverable for new users.
RELEASE.md"Tests + docs for new functionality" section now includes amix docspreview step and clarifies that hexdocs publishing is automatic viamix hex.publish(rides along from the previously-unreleased doc improvement).MOB_PLUGINS.md— plugin manifest schema spec covering five plugin tiers (pure Elixir helper through embedded sub-app), worked examples per tier, install + activation flow, schema reference, validation rules, hot-push compatibility table, plugin_spec_version forward-compat. References from the matrix's ❌ rows as plugin candidates.
[0.6.6]
Added
RELEASE.md— canonical release-process documentation covering the mix.exs-driven trigger model, the patch-bump-default-with-mandatory- permission rule, CHANGELOG conventions, when a bump is warranted (new functionality, bug fixes, doc improvements, dep bumps) vs. when it isn't (CI tweaks, hook changes, internal refactors), the tests-and-docs-with-new-functionality non-negotiables, and the per-step idempotency ofrelease.yml. Linked frommob_devandmob_newCLAUDE.md by URL so the canonical process is one file..githooks/pre-push— committed pre-push hook that runs the cheap preflight (format + credo + warnings-as-errors) on every push and the full release preflight (test suite +mob.security_scanwhere present) only whenmix.exschanged. Activate per-clone withgit config core.hooksPath .githooks.CLAUDE.md"Release flow" section linking to the new docs.
[0.6.5]
Fixed
- HexDocs source links pointed at the non-existent
mainbranch — corrected tomasterso each</>glyph next to a heading now opens the actual source file in the GitHub repo. mob_nif.zigcalled the variadicenif_make_list/2(not exposed inmob_erts.zig) from the BT paired-list finisher; the Android arm64 build failed at link. Switched to the non-variadicenif_make_list_from_array(env, &empty, 0).
Added
.github/workflows/test.yml— runsmix test,mix format --check-formatted,mix credo --strict,mix erlfmt --check src/,xcrun clang-format,swiftlint, 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 (falls back to auto-generated commit notes if the tag has no section).PLAN.md— three-layer CI + integration-test plan covering the gap between unit tests and on-device verification.
[0.6.4]
Added
Mob.GpuView/Mob.UI.gpu_view/1— Metal fragment-shader surface on iOS. Host owns the vertex shader (full-screen quad withv_uv); user supplies an MSL fragment shader plus a list of uniforms packed at natural alignment into fragment-buffer slot 0. SwiftUIMobGpuViewwraps anMTKViewwith a hash-keyed shader cache and a translucent red overlay for compile errors. iOS-only in this release; the Android GLES 3.0 backend ships in mob_new 0.3.1.<GpuView>tag whitelisted for bothpriv/tags/ios.txtandpriv/tags/android.txt.
[0.6.3]
Fixed
- iOS camera sensor delivered frames in landscape-right by default —
Mob.Camera.start_frame_stream/2was feeding 90°-rotated pixels to ML models, dropping classification accuracy enough that a jar appeared as "laptop 24%" instead of "cup 96%".AVCaptureConnection.videoRotationAngle = 90(iOS 17+) /videoOrientation = .portrait(older) is now set on both the preview layer and the data-output connection, so what the user sees and what the model sees are the same upright frame.
[0.6.2]
Added
Mob.Camera.start_frame_stream/2andstop_frame_stream/1— push-driven per-frame delivery as{:camera, :frame, %{bytes, width, height, format, timestamp_ms, dropped}}. Defaults to 640×640rgb_f32for direct Nx hand-off; caller-overridable width/height/format/facing and a softwarethrottle_msgate.
Changed
- iOS camera now uses a single shared
AVCaptureSessionfor preview and frame stream. The previous two-session design silently dropped frames because iOS allows only one active session per physical camera.
[0.6.1] and earlier
Earlier releases predate this changelog; consult the tag list and the per-tag commit messages for history.