mob_mishka

Mishka Chelekom composites for Mob apps, shipped as a proper Hex plugin instead of vendored into every generated app.

Seventy-three composites — dialogs, tabs, sliders, hue/alpha pickers, semi-circle progress, angle slider, and the rest — that used to live as .ex copies inside every mix mob.new project now ship from one place, upgrade like any other dep, and stay swappable for users who want to edit their own copy.

Why this exists

Before this plugin, mob_new baked 75+ Mishka composite files into every generated app's lib/<app>/components/ at generation time. Regenerating an app pulled a fresh snapshot; any user edits to a composite diverged from upstream forever. There was a bespoke mix mob_new.sync_mishka task coupling the mob_new archive to Mishka's source, and a mishka_chelekom mix task that used to write into deps/mob/priv/tags/*.txt to silence sigil warnings.

This plugin replaces all three:

Install

Requires mob 0.9.0 or newer (plugin-manifest tag discovery is what lets the sigil compile <MishkaHueSlider> and its siblings without an extra_tags block).

Add to your Mob app's mix.exs:

def deps do
[
{:mob, "~> 0.9.0"},
{:mob_mishka, "~> 0.1"}
]
end

Then in mob.exs, activate the plugin:

config :mob, :plugins, [:mob_mishka]

That is the entire setup. No Components.register_all/0 call in your on_start/0, no extra_tags config, no deps/mob/priv/tags/*.txt edits — MobMishka.register_all/0 runs from the plugin's manifest :lifecycle.on_start when your app boots.

What's in it

Seventy-three composites plus three support modules (Anchored, Color, Event). A few of the more useful ones:

Composite What it is
<MishkaDialog> Modal dialog with confirm/dismiss buttons
<MishkaTabs> Tab bar with selectable panels
<MishkaAccordion> Expand/collapse panels with open-change events
<MishkaHueSlider> / <MishkaAlphaSlider> HSL/HSV colour pickers on a canvas
<MishkaSlider> Range slider with snap support
<MishkaAngleSlider> Circular dial for a 0–360° angle
<MishkaSemiCircleProgress> Half-circle gauge
<MishkaLoadingOverlay> Full-screen loading indicator
<MishkaSeparator> / <MishkaSpoiler> / <MishkaVisuallyHidden> Layout helpers
<MishkaJsonInput> Validating JSON text editor

Use any of them straight from a screen:

def render(assigns) do
~MOB"""
<Column padding={16}>
<MishkaSemiCircleProgress value={@battery} label="Battery" />
<MishkaAngleSlider value={@angle} on_change={:angle} />
</Column>
"""
end
def handle_info({:tap, :angle, deg}, socket) do
{:noreply, Mob.Socket.assign(socket, :angle, deg)}
end

The full list of registered tags lives in priv/mob_plugin.exs and is the same list Mob.Composite.expanders/0 returns at runtime.

Migrating an existing app

If your app was generated by mob_new before the vendored composites were dropped (mob_new < 0.6), see MIGRATIONS.md. Short version: add the dep, run mix mob_mishka.migrate to preview which vendored composite files can be deleted, then mix mob_mishka.migrate --apply to make the changes. User-edited copies are preserved as overrides.

Also strips the compat config :mob, :extra_tags block from config/config.exs when combined with --remove-extra-tags, once your mob dep supports plugin-manifest tag discovery (0.9.0+).

Ejecting a composite for editing

mix mob_mishka.gen dialog # or: mix mob_mishka.gen mishka_dialog
mix mob_mishka.gen --all # eject every composite the plugin ships

Copies MobMishka.Components.MishkaDialog into lib/<your_app>/components/mishka_dialog.ex as <YourApp>.Components.MishkaDialog. Activate the ejected copies once (config/config.exs):

config :mob_mishka, :override_namespace, YourApp.Components

MobMishka.register_all/0 sees the app-local module at boot and prefers it over the plugin's default, so editing your copy takes effect on the next compile with no re-registration. Delete the file to fall back to the plugin's version.

Sibling aliases inside the ejected file still point at the plugin (e.g. MishkaCloseButton ejected on its own still calls the plugin's MishkaActionIcon). If you also want to edit a sibling, eject it too and fix up the alias by hand.

Design credit

The composites, colour theory, and visual language are Mishka Chelekom's work — see mishka-group/mishka_chelekom. This plugin is the mob-side surface: the Mob composites drawn from the same design intent, plus the plugin-manifest wiring that makes them ship without vendoring.

The Phoenix/LiveView side of Chelekom is untouched by this — only the mob composites moved. Existing mishka_chelekom users see one deprecation cycle on the mob mix tasks that redirect to mob_mishka.

License

MIT.